From cebb168c6fbae7e478f221172a9d6a31c0e71c08 Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Wed, 29 Apr 2026 10:41:44 +0300 Subject: [PATCH 01/24] lifecycle-transitions scheduler bug fixed. --- .cursor/skills/vnext-features/SKILL.md | 777 ++++++++++++++++++ .../lifecycle-transitions-test-workflow.http | 7 + .../postman-lifecycle-transitions.json | 25 + .../postman-smoke-runtime-check.json | 240 ++++++ ...cle-transitions-test-workflow.diagram.json | 44 + .../lifecycle-transitions-test-workflow.json | 42 +- .../src/AlwaysTrueRule.csx | 4 +- .../src/DefaultAutoFallbackRule.csx | 27 + .../src/ProcessEntryMapping.csx | 3 +- .../src/ShortTimerMapping.csx | 5 +- .../src/TestPathDefaultFallbackRule.b64.txt | 1 + .../schema-data-test-workflow.diagram.json | 24 + .../smoke-runtime-check-workflow.json | 113 +++ .../subflow-orchestration-parent.diagram.json | 24 + .../task-execution-test-workflow.diagram.json | 32 + doc/integration-test-documentation.md | 12 +- vnext.config.json | 7 +- 17 files changed, 1340 insertions(+), 47 deletions(-) create mode 100644 .cursor/skills/vnext-features/SKILL.md create mode 100644 api-tests/smoke-runtime-check/postman-smoke-runtime-check.json create mode 100644 core/Workflows/lifecycle-transitions/.meta/lifecycle-transitions-test-workflow.diagram.json create mode 100644 core/Workflows/lifecycle-transitions/src/DefaultAutoFallbackRule.csx create mode 100644 core/Workflows/lifecycle-transitions/src/TestPathDefaultFallbackRule.b64.txt create mode 100644 core/Workflows/schema-data/.meta/schema-data-test-workflow.diagram.json create mode 100644 core/Workflows/smoke-runtime-check/smoke-runtime-check-workflow.json create mode 100644 core/Workflows/subflow-orchestration/.meta/subflow-orchestration-parent.diagram.json create mode 100644 core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json diff --git a/.cursor/skills/vnext-features/SKILL.md b/.cursor/skills/vnext-features/SKILL.md new file mode 100644 index 0000000..fcc6f47 --- /dev/null +++ b/.cursor/skills/vnext-features/SKILL.md @@ -0,0 +1,777 @@ +--- +name: vnext-features +description: Scope is repo-wide domain knowledge (not bound to a single workspace). vNext Runtime platform referansı. Workflow, state, transition, task, schema, view, function ve extension kavramlarını, JSON tanım yapılarını ve C# mapping/rule pattern'lerini kapsar. vNext bileşenleri oluşturulurken veya düzenlenirken bu skill'i kullanın. Trigger this skill whenever vNext workflow/task/schema/view/function/extension JSON or `.csx` mapping/rule files are being created or edited anywhere in the monorepo (or in target vnext domain repositories). +--- + +# vNext Skill + +> **Scope:** Repo-wide domain reference. vNext bileşenleri (workflow, task, schema, view, function, extension JSON ve `.csx` mapping/rule dosyaları) ile çalışırken her workspace'te tetiklenir; tek bir `apps/*` veya `packages/*` ile sınırlı değildir. + +Bu dosya, vNext Runtime'ı hızlı ama eksiksiz sayılabilecek bir çerçevede anlamak için hazırlanmış kompakt bir referanstır. Amaç, vNext ile çalışan birinin platformun ana modelini, JSON tanımlarını, desteklenen yapılarını ve genişleme noktalarını tek yerden kavramasıdır. + +## 1. vNext Nedir? + +vNext, iş akışlarını JSON ile tanımlanan, versiyonlanan ve runtime'da state machine olarak çalışan workflow'lar üzerinden yöneten bir platformdur. + +Temel yaklaşım: + +- Süreçler birer `workflow/flow` olarak tanımlanır. +- Workflow içindeki ilerleme `state` ve `transition` yapıları ile kontrol edilir. +- İş yapan parça `task`'tır. +- Veri kontratı `schema` ile tanımlanır. +- UI veya istemci gösterimi `view` ile verilir. +- Okuma/erişim amaçlı runtime endpoint'leri `function` ile sunulur. +- Response zenginleştirme `extension` ile yapılır. +- Tekrarlı kod yerine `reference` ve `version strategy` kullanılır. + +Kısacası vNext: + +- süreç modelleme, +- süreç orkestrasyonu, +- entegrasyon, +- stateful runtime, +- versiyonlu tanım yönetimi + +işlerini tek çatı altında toplar. + +## 2. Zihinsel Model + +vNext'i doğru anlamanın en kısa yolu şu akışı bilmektir: + +1. Bir workflow JSON'u yayınlanır. +2. Runtime bu workflow'un bir `instance`'ını başlatır. +3. Instance bir state'te durur. +4. Kullanıcı, event, timer veya otomatik kural bir transition tetikler. +5. Transition öncesi/sonrası task'lar çalışır. +6. Veri schema kurallarına göre taşınır ve doğrulanır. +7. Gerekirse subflow/subprocess başlatılır. +8. Runtime state'i değiştirir, view ve function çıktıları buna göre güncellenir. + +Bu yüzden vNext'te ana odak "sayfa" değil, "durumdan duruma ilerleyen süreç"tir. + +## 3. Ana Elemanlar + +### 3.1 Workflow / Flow + +Workflow, sürecin ana tanımıdır. Zorunlu olarak: + +- `type` +- `startTransition` +- `states` + +alanlarını içerir. + +Desteklenen workflow tipleri: + +- `C`: Core +- `F`: Flow +- `S`: SubFlow +- `P`: SubProcess + +Sık kullanılan ek alanlar: + +- `labels` +- `timeout` +- `functions` +- `features` +- `extensions` +- `sharedTransitions` +- `cancel` +- `schema` +- `errorBoundary` + +Ne zaman hangisi: + +- `F`: ana iş akışı +- `S`: parent ile bağlı çalışan alt akış +- `P`: bağımsız, fire-and-forget benzeri alt süreç +- `C`: sistemsel/çekirdek tanımlar + +### 3.2 State + +State, instance'ın o an bulunduğu adımdır. + +State tipleri: + +- `1`: Initial +- `2`: Intermediate +- `3`: Finish +- `4`: SubFlow + +State alt tipleri: + +- `0`: None +- `1`: Success +- `2`: Error +- `3`: Terminated +- `4`: Suspended +- `5`: Busy +- `6`: Human + +Bir state içinde tipik olarak şunlar bulunur: + +- `key` +- `stateType` +- `subType` +- `labels` +- `transitions` +- `onEntries` +- `onExits` +- `view` +- `subFlow` +- `errorBoundary` + +Önemli not: + +- `SubFlow` state'i parent-child ilişki kurar. +- Runtime, alt akışlarda `effectiveState` mantığı ile dışarıya daha anlamlı durum gösterebilir. + +### 3.3 Transition + +Transition, state değişimini başlatan tanımdır. + +Ana alanlar: + +- `key` +- `from` +- `target` +- `triggerType` + +Opsiyonel alanlar: + +- `timer` +- `rule` +- `schema` +- `availableIn` +- `labels` +- `view` +- `mapping` +- `onExecutionTasks` + +Trigger tipleri: + +- `0`: Manual +- `1`: Automatic +- `2`: Scheduled +- `3`: Event + +Önemli davranışlar: + +- `startTransition` başlangıç geçişidir, `from` içermez. +- `availableIn`, ortak transition'ların birden fazla state'te kullanılmasını sağlar. +- `target: "$self"` deseni, özellikle subflow sonrası parent data güncelleme gibi senaryolarda kullanılır. +- Geçiş payload'ı mapping yoksa doğrudan instance data'ya merge edilir. + +### 3.4 Task + +Task, gerçek işi yapan birimdir. vNext'te entegrasyonun ve işlem yürütmenin temel taşıdır. + +Task'lar şu yerlerde çalışabilir: + +- state `onEntries` +- state `onExits` +- transition `onExecutionTasks` +- function tanımları +- extension tanımları + +Çalışma sırası: + +- Aynı `order`: paralel +- Farklı `order`: küçükten büyüğe sıralı + +Ana task aileleri: + +- `Http` +- `DaprService` +- `DaprPubSub` +- `Script` +- `Trigger` +- `GetInstances` +- `Condition` (sistemsel) +- `Timer` (sistemsel) + +Trigger görev ailesinde öne çıkanlar: + +- `StartTask` +- `DirectTriggerTask` +- `GetInstanceDataTask` +- `SubProcessTask` + +Task response tarafında standart olarak şu tip bilgiler bulunur: + +- `Data` +- `StatusCode` +- `IsSuccess` +- `ErrorMessage` +- `Headers` +- `Metadata` +- `ExecutionDurationMs` +- `TaskType` + +Bu yapı sayesinde task'lar sadece çağrı yapmak için değil, workflow orchestration için de kullanılır. + +### 3.5 Schema + +Schema, veri kontratıdır. Hem workflow verisini hem task/function/view gibi tanım tiplerini standardize eder. + +Schema tanımı tipik olarak şu alanları taşır: + +- `key` +- `version` +- `domain` +- `flow` +- `flowVersion` +- `tags` +- `attributes` + +`attributes.type` destekleri: + +- `workflow` +- `task` +- `function` +- `view` +- `schema` +- `extension` +- `headers` + +Schema motoru JSON Schema Draft 2020-12 tabanlıdır. + +Desteklenen ana JSON Schema özellikleri: + +- primitive tipler: `string`, `number`, `integer`, `boolean`, `array`, `object`, `null` +- string doğrulamaları: `minLength`, `maxLength`, `pattern`, `format` +- formatlar: `email`, `uri`, `date`, `date-time`, `time`, `uuid`, `ipv4`, `ipv6` +- sayısal doğrulamalar: `minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum`, `multipleOf` +- array alanları: `items`, `minItems`, `maxItems`, `uniqueItems` +- object alanları: `properties`, `required`, `additionalProperties` +- koşullu yapılar: `if/then/else`, `oneOf`, `anyOf`, `allOf` + +Flow seviyesinde `master schema` kullanılabilir. Bu, instance data'nın kalıcı kontratıdır. Yan ürün geliştirirken en önemli referans çoğu zaman budur. + +Ek olarak: + +- property bazlı görünürlük/authorization kurguları desteklenir. +- transition bazlı schema ile kullanıcı aksiyon payload'ı ayrı doğrulanabilir. + +### 3.6 View + +View, istemcinin ne göstereceğini tanımlar. UI kodu olmak zorunda değildir; render edilebilir bir temsil tanımıdır. + +Ana alanlar: + +- `key` +- `flow` +- `domain` +- `version` +- `type` +- `content` +- `display` +- `labels` +- `platformOverrides` + +Desteklenen view tipleri: + +- `Json` +- `Html` +- `Markdown` +- `DeepLink` +- `Http` +- `URN` + +Display modları: + +- `full-page` +- `popup` +- `bottom-sheet` +- `top-sheet` +- `drawer` +- `inline` + +Platform override destekleri: + +- `web` +- `ios` +- `android` + +View seçimi: + +- state view +- transition view +- rule-based view selection +- platform override +- dil/label seçimi + +Yan ürün geliştirirken view'dan alınması gereken mesaj: + +- vNext UI'yı dikte etmez, +- ancak UI için yapılandırılabilir bir gösterim katmanı sunar. + +### 3.7 Function + +Function, workflow instance'ı okumak veya workflow ile etkileşmek için sistemsel API yüzeyidir. + +Temel built-in function'lar: + +- `state` +- `data` +- `view` + +Ne sağlarlar: + +- `state`: mevcut state, mümkün transition'lar, subflow korelasyonları +- `data`: instance data, etag, extension datası +- `view`: state/transition bazlı view içeriği + +Function'lar: + +- istemcinin runtime iç yapıyı bilmeden veri almasını sağlar, +- long-polling ve ETag gibi senaryoları destekler, +- authorization ve role bazlı görünürlüğe uyum sağlar. + +### 3.8 Extension + +Extension, instance response'unu zenginleştiren yardımcı bileşendir. + +Function ile farkı: + +- Function dış endpoint sunar. +- Extension dış endpoint sunmaz. +- Extension çıktısı `extensions` alanında response'a eklenir. + +Extension tipleri: + +- `1`: Global +- `2`: GlobalAndRequested +- `3`: DefinedFlows +- `4`: DefinedFlowAndRequested + +Scope tipleri: + +- `1`: GetInstance +- `2`: GetAllInstances / history benzeri okuma uçları +- `3`: Everywhere + +Kullanım amacı: + +- harici bilgi ekleme +- hesaplanmış veri ekleme +- profil, limit, referans bilgi, session gibi zenginleştirme + +### 3.9 Mapping + +Mapping, vNext'in veri dönüştürme katmanıdır. Task'a giden veriyi ve task'tan dönen veriyi runtime context ile bağlar. + +Ana mapping türleri: + +- input mapping +- output mapping +- transition mapping +- timer mapping +- condition mapping +- subflow mapping +- subprocess mapping + +Kritik interface'ler: + +- `IMapping` +- `ITimerMapping` +- `ISubProcessMapping` +- `ISubFlowMapping` +- `IConditionMapping` +- `ITransitionMapping` + +Script tarafında tipik çalışma context'i: + +- request body +- headers +- query parameters +- route values +- instance data +- workflow definition +- transition definition +- task response'ları +- metadata + +Script motoru Roslyn/C# tabanlıdır. Native C# ve encode edilmiş script kullanımı desteklenir. + +### 3.10 Error Boundary + +Error handling hiyerarşik çalışır: + +- task seviyesi +- state seviyesi +- global workflow seviyesi + +Ana aksiyonlar: + +- `Abort` +- `Retry` +- `Rollback` +- `Ignore` +- `Notify` +- `Log` + +Retry policy ile: + +- `maxRetries` +- `initialDelay` +- `backoffType` +- `backoffMultiplier` +- `maxDelay` +- `useJitter` + +tanımlanabilir. + +Yan ürün geliştirirken bu önemli çünkü vNext sadece "happy path" değil, hata ve retry akışını da model seviyesinde taşır. + +### 3.11 Reference ve Versioning + +vNext'te tekrar yerine referans kullanılır. Tanımlar birbirine doğrudan gömülmek yerine version'lı reference ile bağlanır. + +Tam referans yapısı: + +```json +{ + "key": "customer-lookup", + "domain": "crm", + "version": "1.2.0", + "flow": "sys-tasks" +} +``` + +Kullanıldığı yerler: + +- task referansı +- subflow referansı +- function referansı +- extension referansı +- schema referansı +- view referansı + +Sistemsel akışlar: + +- `sys-flows` +- `sys-views` +- `sys-functions` +- `sys-tasks` +- `sys-extensions` +- `sys-schemas` + +Versioning yaklaşımı: + +- semver +- explicit version referansı +- version strategy ile latest/major/minor benzeri seçim + +Bu sayede runtime ve tanım yönetimi ayrışır. + +### 3.12 Instance ve Persistence + +Runtime'da çalışan şey tanım değil, `instance`'tır. + +Instance tarafında tipik kavramlar: + +- `id` +- `key` +- `flow` +- `domain` +- `flowVersion` +- `etag` +- `tags` +- `attributes` +- `metadata` +- `data` +- `extensions` + +Persistence modeli özetle: + +- workflow instance kayıtları master data tarafında tutulur, +- transition/task/action/job geçmişi ayrı tablolarda izlenebilir, +- domain bazlı veritabanı ayrımı bulunur. + +Bu yapı vNext'i tek bir workflow engine olmaktan çıkarıp çok domain'li enterprise runtime haline getirir. + +## 4. JSON Nasıl Oluşturulur? + +vNext'te çoğu tanım ortak bir zarf yapısı kullanır: + +```json +{ + "key": "my-definition", + "version": "1.0.0", + "domain": "banking", + "flow": "sys-flows", + "flowVersion": "1.0.0", + "tags": ["banking", "example"], + "attributes": {} +} +``` + +Pratikte dikkat edilmesi gerekenler: + +- `key`: benzersiz ve okunabilir olsun +- `version`: semver olsun +- `domain`: sahiplik alanını anlatsın +- `flow`: bunun hangi sistem koleksiyonuna ait olduğunu göstersin +- `attributes`: asıl işi burada tanımlarsın + +## 5. Minimal JSON İskeletleri + +### 5.1 Minimal Workflow + +```json +{ + "key": "account-opening", + "version": "1.0.0", + "domain": "banking", + "flow": "sys-flows", + "flowVersion": "1.0.0", + "tags": ["banking", "onboarding"], + "attributes": { + "type": "F", + "startTransition": { + "key": "start", + "target": "draft", + "triggerType": 1 + }, + "states": [ + { + "key": "draft", + "stateType": 1, + "subType": 6, + "transitions": [ + { + "key": "submit", + "from": "draft", + "target": "completed", + "triggerType": 0 + } + ] + }, + { + "key": "completed", + "stateType": 3, + "subType": 1 + } + ] + } +} +``` + +### 5.2 Transition İçinde Task Kullanımı + +```json +{ + "key": "submit", + "from": "draft", + "target": "completed", + "triggerType": 0, + "onExecutionTasks": [ + { + "order": 1, + "task": { + "key": "notify-customer", + "domain": "notification", + "version": "1.0.0", + "flow": "sys-tasks" + } + } + ] +} +``` + +### 5.3 Minimal Schema + +```json +{ + "key": "account-opening-master-schema", + "version": "1.0.0", + "domain": "banking", + "flow": "sys-schemas", + "flowVersion": "1.0.0", + "tags": ["workflow", "schema"], + "attributes": { + "type": "workflow", + "schema": { + "type": "object", + "properties": { + "customerId": { "type": "string" }, + "amount": { "type": "number", "minimum": 0 } + }, + "required": ["customerId"] + } + } +} +``` + +### 5.4 Minimal View + +```json +{ + "key": "draft-form-view", + "version": "1.0.0", + "domain": "banking", + "flow": "sys-views", + "flowVersion": "1.0.0", + "tags": ["view"], + "attributes": { + "type": "Json", + "display": "full-page", + "content": { + "component": "AccountOpeningForm" + } + } +} +``` + +### 5.5 Minimal Extension + +```json +{ + "key": "extension-customer-profile", + "version": "1.0.0", + "domain": "crm", + "flow": "sys-extensions", + "flowVersion": "1.0.0", + "tags": ["profile"], + "attributes": { + "type": 4, + "scope": 1, + "task": { + "order": 1, + "task": { + "key": "get-customer-profile", + "domain": "crm", + "version": "1.0.0", + "flow": "sys-tasks" + } + } + } +} +``` + +## 6. vNext Hangi Yapıları ve Feature'ları Destekliyor? + +### 6.1 Süreç Modelleme + +- version'lı workflow +- state machine +- start transition +- finish state +- manual/auto/event/scheduled transition +- shared transition +- cancel flow +- timeout +- subflow +- subprocess +- parent-child correlation + +### 6.2 Orkestrasyon ve Entegrasyon + +- HTTP çağrıları +- Dapr service invocation +- Dapr pub/sub +- script execution +- başka workflow başlatma +- doğrudan transition tetikleme +- instance data okuma +- instance listeleme / filtreleme +- timer ve condition tabanlı otomasyon + +### 6.3 Veri ve Doğrulama + +- JSON Schema Draft 2020-12 +- master schema +- transition schema +- headers schema +- field-level visibility +- camelCase veri kullanım pratiği +- mapping ile custom payload transform + +### 6.4 UI ve Client Entegrasyonu + +- state view +- transition view +- rule-based view selection +- Json/Html/Markdown/DeepLink/Http/URN view tipleri +- display modları +- platform override +- localization/labels + +### 6.5 Runtime API Yetenekleri + +- state polling +- data fetch +- view fetch +- permissions/authorize endpoint'leri +- ETag / conditional fetch +- extension ile enriched response + +### 6.6 Kurumsal Özellikler + +- semver ve version strategy +- domain bazlı ayrışma +- ayrı veritabanı / schema yapıları +- audit/history izleme +- retry/backoff/jitter +- role/queryRole bazlı yetkilendirme +- hata boundary hiyerarşisi + +## 7. Yan Ürün Geliştirecek Biri İçin Yol Haritası + +vNext üzerinde yan ürün geliştirirken genelde şu giriş noktalarından biri kullanılır: + +- workflow JSON üretici/editörü +- schema/view validator +- runtime client SDK +- instance monitor dashboard +- transition execution paneli +- workflow diff/version karşılaştırıcı +- task kütüphanesi yöneticisi +- extension/function katalog aracı + +En kritik doğrular: + +- Süreci state machine olarak düşün. +- Kalıcı veri kontratını `master schema` üzerinden kur. +- Tekrarlı gömme yerine `reference` kullan. +- UI'ı workflow'dan değil, `view + state + schema` üçlüsünden türet. +- Entegrasyon mantığını `task + mapping` ile taşı. +- Okuma API'leri için `function`, response zenginleştirme için `extension` kullan. + +## 8. Kısa Tasarım Kuralları + +- Workflow'ları küçük ve anlamlı tut. +- State isimleri business meaning taşısın. +- Transition isimleri aksiyon fiili gibi olsun. +- Task'ları tekrar kullanılabilir tasarla. +- Aynı concern'ü function ve extension arasında karıştırma. +- Schema'yı gerçeğin kaynağı kabul et. +- Subflow ile subprocess farkını net koru: biri bağlı, biri bağımsız. +- Error boundary tanımını en yakın seviyede çöz, global'i fallback olarak bırak. + +## 9. Sonuç + +vNext, JSON tabanlı, versiyonlu, stateful bir workflow runtime platformudur. Ana yapı taşları: + +- workflow +- state +- transition +- task +- schema +- view +- function +- extension +- mapping +- reference/versioning +- instance/persistence +- error boundary + +Bir kişi bu dosyayı baz alarak: + +- vNext'in neyi çözdüğünü, +- workflow JSON'un nasıl kurulduğunu, +- hangi feature'ların desteklendiğini, +- nereden genişletme yapılacağını + +anlayıp vNext için araç, entegrasyon, editör, SDK veya izleme ürünü geliştirmeye başlayabilir. diff --git a/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http b/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http index cdad213..9d64069 100644 --- a/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http +++ b/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http @@ -30,8 +30,10 @@ Content-Type: application/json GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{passInstanceId}}/functions/state ### Adim 3: Manuel Transition - Move to Processing (ITransitionMapping test) +### transition roles: allow sadece test-operator (rolsuz cagri -> instance status F) PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{passInstanceId}}/transitions/move-to-processing?sync=true Content-Type: application/json +role: test-operator {} @@ -43,8 +45,10 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{passInstanceId}}/functions/state ### Adim 6: Manuel Transition - Complete Workflow +### transition roles: allow test-approver; test-operator deny PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{passInstanceId}}/transitions/complete-workflow?sync=true Content-Type: application/json +role: test-approver {} @@ -74,6 +78,7 @@ Content-Type: application/json ### Adim 2: Manuel Transition - Move to Processing PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{failInstanceId}}/transitions/move-to-processing?sync=true Content-Type: application/json +role: test-operator {} @@ -103,6 +108,7 @@ Content-Type: application/json ### Adim 2: Manuel Transition - Move to Processing PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{defaultInstanceId}}/transitions/move-to-processing?sync=true Content-Type: application/json +role: test-operator {} @@ -300,6 +306,7 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/ ### Adim 3: move-to-processing transition (test-operator rolu ile) PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/transitions/move-to-processing Content-Type: application/json +role: test-operator {} diff --git a/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json b/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json index f97b393..4f30506 100644 --- a/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json +++ b/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json @@ -124,6 +124,11 @@ "key": "Content-Type", "value": "application/json", "type": "text" + }, + { + "key": "role", + "value": "test-operator", + "type": "text" } ], "url": { @@ -220,6 +225,11 @@ "key": "Content-Type", "value": "application/json", "type": "text" + }, + { + "key": "role", + "value": "test-approver", + "type": "text" } ], "url": { @@ -355,6 +365,11 @@ "key": "Content-Type", "value": "application/json", "type": "text" + }, + { + "key": "role", + "value": "test-operator", + "type": "text" } ], "url": { @@ -490,6 +505,11 @@ "key": "Content-Type", "value": "application/json", "type": "text" + }, + { + "key": "role", + "value": "test-operator", + "type": "text" } ], "url": { @@ -1350,6 +1370,11 @@ "key": "Content-Type", "value": "application/json", "type": "text" + }, + { + "key": "role", + "value": "test-operator", + "type": "text" } ], "url": { diff --git a/api-tests/smoke-runtime-check/postman-smoke-runtime-check.json b/api-tests/smoke-runtime-check/postman-smoke-runtime-check.json new file mode 100644 index 0000000..96eabe9 --- /dev/null +++ b/api-tests/smoke-runtime-check/postman-smoke-runtime-check.json @@ -0,0 +1,240 @@ +{ + "info": { + "_postman_id": "a1111111-b222-4c33-d444-555566667777", + "name": "vNext Smoke - Minimal Runtime Check", + "description": "En basit workflow (smoke-runtime-check-workflow): script yok, auto transition yok, transition rol yok.\nAkis: baslat — durum — finish-smoke PATCH — son durum.\nRuntime PATCH/GET dongusunu yalitmak icin.\nOn kosul: workflow publish (core/smoke-runtime-check-workflow).", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "variable": [ + { + "key": "baseUrl", + "value": "http://localhost:4201", + "type": "string" + }, + { + "key": "apiVersion", + "value": "1", + "type": "string" + }, + { + "key": "domain", + "value": "core", + "type": "string" + }, + { + "key": "workflow", + "value": "smoke-runtime-check-workflow", + "type": "string" + } + ], + "item": [ + { + "name": "Minimal smoke (manuel tek gecis)", + "item": [ + { + "name": "1 Baslat POST /instances/start ?sync=true", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"smoke-test-{{$timestamp}}\",\n \"tags\": [\"smoke\", \"minimal\"],\n \"attributes\": {\n \"ping\": true\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "if (jsonData && jsonData.id) {", + " pm.collectionVariables.set(\"smokeInstanceId\", jsonData.id);", + " console.log(\"smokeInstanceId:\", jsonData.id);", + "} else {", + " console.warn(\"Baslat yaniti id icermiyor\", jsonData);", + "}", + "pm.test(\"HTTP 200-299\", function () { pm.response.to.have.status(200); });" + ], + "type": "text/javascript" + } + } + ] + }, + { + "name": "2 GET /functions/state (F olmamali, smoke-start)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{smokeInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{smokeInstanceId}}", + "functions", + "state" + ] + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var j = pm.response.json();", + "console.log(\"state yaniti\", JSON.stringify(j));", + "pm.test(\"status F olmamalidir\", function () {", + " pm.expect(j.status === undefined ? null : String(j.status)).to.not.eql(\"F\");", + "});", + "var stateKey = (j.effectiveState && j.effectiveState.key) || (j.state && j.state.key) || j.key;", + "if (stateKey) { console.log(\"state key:\", stateKey); pm.test(\"smoke-start bekleniyor\", function () { pm.expect(String(stateKey)).to.eql(\"smoke-start\"); }); }" + ], + "type": "text/javascript" + } + } + ] + }, + { + "name": "3 PATCH finish-smoke ?sync=true", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{smokeInstanceId}}/transitions/finish-smoke?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{smokeInstanceId}}", + "transitions", + "finish-smoke" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"PATCH 200\", function () { pm.response.to.have.status(200); });" + ], + "type": "text/javascript" + } + } + ] + }, + { + "name": "4 GET /functions/state (final smoke-done)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{smokeInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{smokeInstanceId}}", + "functions", + "state" + ] + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var j = pm.response.json();", + "console.log(\"final state\", JSON.stringify(j));", + "pm.test(\"status F olmamalidir\", function () {", + " pm.expect(j.status === undefined ? null : String(j.status)).to.not.eql(\"F\");", + "});", + "var stateKey = (j.effectiveState && j.effectiveState.key) || (j.state && j.state.key);", + "if (stateKey) { pm.test(\"smoke-done final\", function () { pm.expect(String(stateKey)).to.eql(\"smoke-done\"); }); }" + ], + "type": "text/javascript" + } + } + ] + } + ] + } + ] +} diff --git a/core/Workflows/lifecycle-transitions/.meta/lifecycle-transitions-test-workflow.diagram.json b/core/Workflows/lifecycle-transitions/.meta/lifecycle-transitions-test-workflow.diagram.json new file mode 100644 index 0000000..1dcccf8 --- /dev/null +++ b/core/Workflows/lifecycle-transitions/.meta/lifecycle-transitions-test-workflow.diagram.json @@ -0,0 +1,44 @@ +{ + "nodePos": { + "initialize-state": { + "x": 440, + "y": 300 + }, + "processing-state": { + "x": 860, + "y": 120 + }, + "auto-passed-state": { + "x": 1340, + "y": -80 + }, + "timer-triggered-state": { + "x": 1640, + "y": 540 + }, + "pre-complete-state": { + "x": 2140, + "y": 240 + }, + "default-fallback-state": { + "x": 2180, + "y": -40 + }, + "auto-failed-state": { + "x": 1120, + "y": 700 + }, + "completed-state": { + "x": 2820, + "y": 240 + }, + "terminated-state": { + "x": 240, + "y": -200 + }, + "__start__": { + "x": 100, + "y": 300 + } + } +} \ No newline at end of file diff --git a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json index a965092..5879e5f 100644 --- a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json +++ b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json @@ -3,7 +3,7 @@ "flow": "sys-flows", "flowVersion": "1.0.0", "domain": "core", - "version": "1.0.0", + "version": "1.4.0", "tags": [ "integration-test", "lifecycle", @@ -56,6 +56,8 @@ "target": "terminated-state", "triggerType": 0, "versionStrategy": "Major", + "availableIn": [], + "onExecutionTasks": [], "labels": [ { "language": "en-US", @@ -176,13 +178,7 @@ "location": "./src/ProcessTransitionMapping.csx", "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBQcm9jZXNzVHJhbnNpdGlvbk1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJVHJhbnNpdGlvbk1hcHBpbmcKewogICAgcHVibGljIGFzeW5jIFRhc2s8ZHluYW1pYz4gSGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CgogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0UGF0aCIpKQogICAgICAgICAgICByZXN1bHQudGVzdFBhdGggPSBkYXRhLnRlc3RQYXRoOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaW5pdGlhbGl6ZWQiKSkKICAgICAgICAgICAgcmVzdWx0LmluaXRpYWxpemVkID0gZGF0YS5pbml0aWFsaXplZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImluaXRpYWxpemVkQXQiKSkKICAgICAgICAgICAgcmVzdWx0LmluaXRpYWxpemVkQXQgPSBkYXRhLmluaXRpYWxpemVkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGVwTG9nIikpCiAgICAgICAgICAgIHJlc3VsdC5zdGVwTG9nID0gZGF0YS5zdGVwTG9nOwoKICAgICAgICByZXN1bHQudHJhbnNpdGlvbk1hcHBpbmdFeGVjdXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnRyYW5zaXRpb25NYXBwaW5nQXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlByb2Nlc3NUcmFuc2l0aW9uTWFwcGluZyBleGVjdXRlZCIpOwoKICAgICAgICByZXR1cm4gcmVzdWx0OwogICAgfQp9Cg==" }, - "onExecutionTasks": [], - "roles": [ - { - "role": "test-operator", - "grant": "allow" - } - ] + "onExecutionTasks": [] } ] }, @@ -224,7 +220,7 @@ }, "mapping": { "location": "./src/ProcessEntryMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFByb2Nlc3NFbnRyeU1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwoKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdFBhdGgiKSkKICAgICAgICAgICAgcmVzdWx0LnRlc3RQYXRoID0gZGF0YS50ZXN0UGF0aDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImluaXRpYWxpemVkIikpCiAgICAgICAgICAgIHJlc3VsdC5pbml0aWFsaXplZCA9IGRhdGEuaW5pdGlhbGl6ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpbml0aWFsaXplZEF0IikpCiAgICAgICAgICAgIHJlc3VsdC5pbml0aWFsaXplZEF0ID0gZGF0YS5pbml0aWFsaXplZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidHJhbnNpdGlvbk1hcHBpbmdFeGVjdXRlZCIpKQogICAgICAgICAgICByZXN1bHQudHJhbnNpdGlvbk1hcHBpbmdFeGVjdXRlZCA9IGRhdGEudHJhbnNpdGlvbk1hcHBpbmdFeGVjdXRlZDsKCiAgICAgICAgcmVzdWx0LnByb2Nlc3NFbnRyeUV4ZWN1dGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQucHJvY2Vzc2VkQXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKCiAgICAgICAgTG9nSW5mb3JtYXRpb24oJCJQcm9jZXNzRW50cnlNYXBwaW5nIGV4ZWN1dGVkLCB0ZXN0UGF0aD17cmVzdWx0LnRlc3RQYXRofSIpOwoKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFByb2Nlc3NFbnRyeU1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwoKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdFBhdGgiKSkKICAgICAgICAgICAgcmVzdWx0LnRlc3RQYXRoID0gZGF0YS50ZXN0UGF0aDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImluaXRpYWxpemVkIikpCiAgICAgICAgICAgIHJlc3VsdC5pbml0aWFsaXplZCA9IGRhdGEuaW5pdGlhbGl6ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpbml0aWFsaXplZEF0IikpCiAgICAgICAgICAgIHJlc3VsdC5pbml0aWFsaXplZEF0ID0gZGF0YS5pbml0aWFsaXplZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidHJhbnNpdGlvbk1hcHBpbmdFeGVjdXRlZCIpKQogICAgICAgICAgICByZXN1bHQudHJhbnNpdGlvbk1hcHBpbmdFeGVjdXRlZCA9IGRhdGEudHJhbnNpdGlvbk1hcHBpbmdFeGVjdXRlZDsKCiAgICAgICAgcmVzdWx0LnByb2Nlc3NFbnRyeUV4ZWN1dGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQucHJvY2Vzc2VkQXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKCiAgICAgICAgdmFyIHRlc3RQYXRoRm9yTG9nID0gSGFzUHJvcGVydHkocmVzdWx0LCAidGVzdFBhdGgiKSA/IHJlc3VsdC50ZXN0UGF0aD8uVG9TdHJpbmcoKSA6ICIobnVsbCkiOwogICAgICAgIExvZ0luZm9ybWF0aW9uKCJQcm9jZXNzRW50cnlNYXBwaW5nIGV4ZWN1dGVkLCB0ZXN0UGF0aD0iICsgdGVzdFBhdGhGb3JMb2cpOwoKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } ], @@ -295,16 +291,16 @@ "labels": [ { "language": "en-US", - "label": "Default Auto (triggerKind:10 fallback)" + "label": "Default Auto (DefaultAutoFallbackRule + triggerKind:10)" }, { "language": "tr-TR", - "label": "Varsayilan Otomatik Gecis (triggerKind:10)" + "label": "Varsayilan Otomatik (DefaultAutoFallbackRule + triggerKind:10)" } ], "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBhc3luYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KfQo=" + "location": "./src/DefaultAutoFallbackRule.csx", + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCi8vIHByb2Nlc3Npbmctc3RhdGU6IG9ubHkgdHJ1ZSB3aGVuIHRlc3RQYXRoIGlzIG5vdCBwYXNzIG5vciBmYWlsIChjb21wbGVtZW50cyBUZXN0UGF0aFBhc3NSdWxlIC8gVGVzdFBhdGhGYWlsUnVsZSkuCnB1YmxpYyBjbGFzcyBEZWZhdWx0QXV0b0ZhbGxiYWNrUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBhc3luYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZT8uRGF0YTsKICAgICAgICBpZiAoZGF0YSA9PSBudWxsKQogICAgICAgIHsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIkRlZmF1bHRBdXRvRmFsbGJhY2tSdWxlOiBkYXRhIG51bGwsIGZhbGxiYWNrIHRydWUiKTsKICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfQoKICAgICAgICBpZiAoIUhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0UGF0aCIpKQogICAgICAgIHsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIkRlZmF1bHRBdXRvRmFsbGJhY2tSdWxlOiB0ZXN0UGF0aCBhYnNlbnQsIGZhbGxiYWNrIHRydWUiKTsKICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfQoKICAgICAgICB2YXIgdGVzdFBhdGggPSBkYXRhLnRlc3RQYXRoPy5Ub1N0cmluZygpOwogICAgICAgIHZhciBuZWl0aGVyID0gdGVzdFBhdGggIT0gInBhc3MiICYmIHRlc3RQYXRoICE9ICJmYWlsIjsKICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkRlZmF1bHRBdXRvRmFsbGJhY2tSdWxlOiB0ZXN0UGF0aD17dGVzdFBhdGh9LCBuZWl0aGVyUGFzc05vckZhaWw9e25laXRoZXJ9Iik7CiAgICAgICAgcmV0dXJuIG5laXRoZXI7CiAgICB9Cn0K" }, "onExecutionTasks": [] } @@ -347,7 +343,7 @@ ], "timer": { "location": "./src/ShortTimerMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFNob3J0VGltZXJNYXBwaW5nIDogU2NyaXB0QmFzZSwgSVRpbWVyTWFwcGluZwp7CiAgICBwdWJsaWMgYXN5bmMgVGFzazxUaW1lclNjaGVkdWxlPiBIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU2hvcnRUaW1lck1hcHBpbmc6IHNjaGVkdWxpbmcgMTAgc2Vjb25kcyBmcm9tIG5vdyIpOwogICAgICAgIHJldHVybiBuZXcgVGltZXJTY2hlZHVsZQogICAgICAgIHsKICAgICAgICAgICAgRmlyZUF0ID0gRGF0ZVRpbWUuVXRjTm93LkFkZFNlY29uZHMoMTApCiAgICAgICAgfTsKICAgIH0KfQo=" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFNob3J0VGltZXJNYXBwaW5nIDogU2NyaXB0QmFzZSwgSVRpbWVyTWFwcGluZwp7CiAgICBwdWJsaWMgYXN5bmMgVGFzazxUaW1lclNjaGVkdWxlPiBIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU2hvcnRUaW1lck1hcHBpbmc6IHNjaGVkdWxpbmcgMTAgc2Vjb25kcyBmcm9tIG5vdyIpOwogICAgICAgIHJldHVybiBUaW1lclNjaGVkdWxlLkZyb21EYXRlVGltZShEYXRlVGltZS5VdGNOb3cuQWRkU2Vjb25kcygxMCkpOwogICAgfQp9Cg==" }, "onExecutionTasks": [] }, @@ -369,7 +365,7 @@ }, { "key": "reschedule-timer", - "target": "$self", + "target": "auto-passed-state", "triggerType": 0, "versionStrategy": "Minor", "labels": [ @@ -436,7 +432,7 @@ ], "rule": { "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBhc3luYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KfQo=" + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" }, "onExecutionTasks": [] } @@ -479,17 +475,7 @@ ], "schema": null, "view": null, - "onExecutionTasks": [], - "roles": [ - { - "role": "test-approver", - "grant": "allow" - }, - { - "role": "test-operator", - "grant": "deny" - } - ] + "onExecutionTasks": [] } ] }, @@ -530,7 +516,7 @@ ], "rule": { "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBhc3luYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KfQo=" + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" }, "onExecutionTasks": [] } diff --git a/core/Workflows/lifecycle-transitions/src/AlwaysTrueRule.csx b/core/Workflows/lifecycle-transitions/src/AlwaysTrueRule.csx index 37de1d3..3d08a0f 100644 --- a/core/Workflows/lifecycle-transitions/src/AlwaysTrueRule.csx +++ b/core/Workflows/lifecycle-transitions/src/AlwaysTrueRule.csx @@ -3,9 +3,9 @@ using BBT.Workflow.Scripting; public class AlwaysTrueRule : ScriptBase, IConditionMapping { - public async Task Handler(ScriptContext context) + public Task Handler(ScriptContext context) { LogInformation("AlwaysTrueRule: returning true"); - return true; + return Task.FromResult(true); } } diff --git a/core/Workflows/lifecycle-transitions/src/DefaultAutoFallbackRule.csx b/core/Workflows/lifecycle-transitions/src/DefaultAutoFallbackRule.csx new file mode 100644 index 0000000..bfc0e7c --- /dev/null +++ b/core/Workflows/lifecycle-transitions/src/DefaultAutoFallbackRule.csx @@ -0,0 +1,27 @@ +using System.Threading.Tasks; +using BBT.Workflow.Scripting; + +// processing-state: only true when testPath is not pass nor fail (complements TestPathPassRule / TestPathFailRule). +public class DefaultAutoFallbackRule : ScriptBase, IConditionMapping +{ + public async Task Handler(ScriptContext context) + { + var data = context.Instance?.Data; + if (data == null) + { + LogInformation("DefaultAutoFallbackRule: data null, fallback true"); + return true; + } + + if (!HasProperty(data, "testPath")) + { + LogInformation("DefaultAutoFallbackRule: testPath absent, fallback true"); + return true; + } + + var testPath = data.testPath?.ToString(); + var neither = testPath != "pass" && testPath != "fail"; + LogInformation($"DefaultAutoFallbackRule: testPath={testPath}, neitherPassNorFail={neither}"); + return neither; + } +} diff --git a/core/Workflows/lifecycle-transitions/src/ProcessEntryMapping.csx b/core/Workflows/lifecycle-transitions/src/ProcessEntryMapping.csx index 6017e51..98a354f 100644 --- a/core/Workflows/lifecycle-transitions/src/ProcessEntryMapping.csx +++ b/core/Workflows/lifecycle-transitions/src/ProcessEntryMapping.csx @@ -29,7 +29,8 @@ public class ProcessEntryMapping : ScriptBase, IMapping result.processEntryExecuted = true; result.processedAt = DateTime.UtcNow.ToString("o"); - LogInformation($"ProcessEntryMapping executed, testPath={result.testPath}"); + var testPathForLog = HasProperty(result, "testPath") ? result.testPath?.ToString() : "(null)"; + LogInformation("ProcessEntryMapping executed, testPath=" + testPathForLog); return Task.FromResult(new ScriptResponse { Data = result }); } diff --git a/core/Workflows/lifecycle-transitions/src/ShortTimerMapping.csx b/core/Workflows/lifecycle-transitions/src/ShortTimerMapping.csx index 945e5a1..5021443 100644 --- a/core/Workflows/lifecycle-transitions/src/ShortTimerMapping.csx +++ b/core/Workflows/lifecycle-transitions/src/ShortTimerMapping.csx @@ -7,9 +7,6 @@ public class ShortTimerMapping : ScriptBase, ITimerMapping public async Task Handler(ScriptContext context) { LogInformation("ShortTimerMapping: scheduling 10 seconds from now"); - return new TimerSchedule - { - FireAt = DateTime.UtcNow.AddSeconds(10) - }; + return TimerSchedule.FromDateTime(DateTime.UtcNow.AddSeconds(10)); } } diff --git a/core/Workflows/lifecycle-transitions/src/TestPathDefaultFallbackRule.b64.txt b/core/Workflows/lifecycle-transitions/src/TestPathDefaultFallbackRule.b64.txt new file mode 100644 index 0000000..39f8a98 --- /dev/null +++ b/core/Workflows/lifecycle-transitions/src/TestPathDefaultFallbackRule.b64.txt @@ -0,0 +1 @@ +dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCi8vLyA8c3VtbWFyeT4KLy8vIHByb2Nlc3Npbmctc3RhdGUgaWNpbmRlIGF1dG8tcGFzcyB2ZSBhdXRvLWZhaWwgaWxlIGJpcmxpa3RlIGt1bGxhbmlsaXIuCi8vLyBBbHdheXNUcnVlIHllcmluZTogc2FkZWNlIHRlc3RQYXRoIG5lICJwYXNzIiBuZSAiZmFpbCIgaXNlIHRydWUgKG9yLiAidW5rbm93biIpLAovLy8gYm95bGVjZSBwYXNzIHlvbHVuZGEgdGVrIGdlY2VybGkgYXV0byBnZWNpcyBhdXRvLXBhc3Mgb2x1cjsgY2lmdCBlc2xlc21lIC8gRiBvbmxlbmlyLgovLy8gPC9zdW1tYXJ5PgpwdWJsaWMgY2xhc3MgVGVzdFBhdGhEZWZhdWx0RmFsbGJhY2tSdWxlIDogU2NyaXB0QmFzZSwgSUNvbmRpdGlvbk1hcHBpbmcKewogICAgcHVibGljIGFzeW5jIFRhc2s8Ym9vbD4gSGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhOwogICAgICAgIGlmIChkYXRhID09IG51bGwpCiAgICAgICAgewogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiVGVzdFBhdGhEZWZhdWx0RmFsbGJhY2tSdWxlOiBkYXRhIGlzIG51bGwiKTsKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0UGF0aCIpKQogICAgICAgIHsKICAgICAgICAgICAgdmFyIHRlc3RQYXRoID0gZGF0YS50ZXN0UGF0aD8uVG9TdHJpbmcoKTsKICAgICAgICAgICAgdmFyIGlzRXhwbGljaXRQYXNzT3JGYWlsID0gdGVzdFBhdGggPT0gInBhc3MiIHx8IHRlc3RQYXRoID09ICJmYWlsIjsKICAgICAgICAgICAgdmFyIHJlc3VsdCA9ICFpc0V4cGxpY2l0UGFzc09yRmFpbDsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oJCJUZXN0UGF0aERlZmF1bHRGYWxsYmFja1J1bGU6IHRlc3RQYXRoPXt0ZXN0UGF0aH0sIHJlc3VsdD17cmVzdWx0fSIpOwogICAgICAgICAgICByZXR1cm4gcmVzdWx0OwogICAgICAgIH0KCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlRlc3RQYXRoRGVmYXVsdEZhbGxiYWNrUnVsZTogdGVzdFBhdGggbWlzc2luZywgcmV0dXJuaW5nIHRydWUgZm9yIGZhbGxiYWNrIik7CiAgICAgICAgcmV0dXJuIHRydWU7CiAgICB9Cn0K \ No newline at end of file diff --git a/core/Workflows/schema-data/.meta/schema-data-test-workflow.diagram.json b/core/Workflows/schema-data/.meta/schema-data-test-workflow.diagram.json new file mode 100644 index 0000000..06f5dd6 --- /dev/null +++ b/core/Workflows/schema-data/.meta/schema-data-test-workflow.diagram.json @@ -0,0 +1,24 @@ +{ + "nodePos": { + "data-initialized": { + "x": 60, + "y": 560 + }, + "schema-validated-state": { + "x": 560, + "y": -260 + }, + "no-schema-state": { + "x": 880, + "y": 580 + }, + "completed-state": { + "x": 1380, + "y": -20 + }, + "__start__": { + "x": -296, + "y": 140 + } + } +} \ No newline at end of file diff --git a/core/Workflows/smoke-runtime-check/smoke-runtime-check-workflow.json b/core/Workflows/smoke-runtime-check/smoke-runtime-check-workflow.json new file mode 100644 index 0000000..4f33da7 --- /dev/null +++ b/core/Workflows/smoke-runtime-check/smoke-runtime-check-workflow.json @@ -0,0 +1,113 @@ +{ + "key": "smoke-runtime-check-workflow", + "flow": "sys-flows", + "flowVersion": "1.0.0", + "domain": "core", + "version": "1.0.0", + "tags": [ + "smoke", + "diagnostic", + "runtime-check", + "minimal" + ], + "attributes": { + "type": "F", + "timeout": null, + "labels": [ + { + "language": "en-US", + "label": "Smoke Runtime Check (minimal manual flow)" + }, + { + "language": "tr-TR", + "label": "Runtime Smoke Testi" + } + ], + "functions": [], + "features": [], + "extensions": [], + "sharedTransitions": [], + "startTransition": { + "key": "start-smoke", + "target": "smoke-start", + "triggerType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Start" + }, + { + "language": "tr-TR", + "label": "Baslat" + } + ], + "onExecutionTasks": [] + }, + "states": [ + { + "key": "smoke-start", + "stateType": 1, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Smoke Start" + }, + { + "language": "tr-TR", + "label": "Baslangic" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "finish-smoke", + "target": "smoke-done", + "triggerType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Finish (manual only)" + }, + { + "language": "tr-TR", + "label": "Bitir" + } + ], + "schema": null, + "view": null, + "mapping": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "smoke-done", + "stateType": 3, + "subType": 1, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Smoke Done" + }, + { + "language": "tr-TR", + "label": "Tamamlandi" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [] + } + ] + } +} diff --git a/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-parent.diagram.json b/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-parent.diagram.json new file mode 100644 index 0000000..9e8a142 --- /dev/null +++ b/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-parent.diagram.json @@ -0,0 +1,24 @@ +{ + "nodePos": { + "parent-initial": { + "x": 496, + "y": 240 + }, + "parent-subflow-state": { + "x": 940, + "y": 480 + }, + "parent-completed": { + "x": 1348, + "y": 240 + }, + "parent-cancelled": { + "x": 100, + "y": 100 + }, + "__start__": { + "x": 100, + "y": 240 + } + } +} \ No newline at end of file diff --git a/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json b/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json new file mode 100644 index 0000000..3aa97de --- /dev/null +++ b/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json @@ -0,0 +1,32 @@ +{ + "nodePos": { + "http-task-state": { + "x": 340, + "y": -180 + }, + "script-processing-state": { + "x": 500, + "y": 560 + }, + "cross-workflow-state": { + "x": 980, + "y": 260 + }, + "start-flow-state": { + "x": 1200, + "y": -220 + }, + "get-instance-data-state": { + "x": 1880, + "y": 280 + }, + "completed-state": { + "x": 2480, + "y": 60 + }, + "__start__": { + "x": -296, + "y": 100 + } + } +} \ No newline at end of file diff --git a/doc/integration-test-documentation.md b/doc/integration-test-documentation.md index 9ef439a..a15734c 100644 --- a/doc/integration-test-documentation.md +++ b/doc/integration-test-documentation.md @@ -42,7 +42,7 @@ core/ **Workflow:** `lifecycle-transitions-test-workflow` (type: F) -**Amac:** Workflow yasam dongusu ve tum transition tiplerini test eder; ayrica **exit transition**, **zamanlayici schedule yonetimi** (manuel iptal ve `$self` ile reschedule), **workflow/state queryRoles** ve **transition bazli roller** senaryolarini kapsar. +**Amac:** Workflow yasam dongusu ve tum transition tiplerini test eder; ayrica **exit transition**, **zamanlayici schedule yonetimi** (manuel iptal ve **self-loop reschedule** — hedef olarak mevcut state anahtari `auto-passed-state`; `$self` ile ayni davranis), **workflow/state queryRoles** ve **transition bazli roller** senaryolarini kapsar. ### Agac Yapisi @@ -90,8 +90,8 @@ lifecycle-transitions-test-workflow (F) │ │ └── timer: ShortTimerMapping.csx (ITimerMapping, 10 saniye) │ ├── cancel-schedule-manually (Manual, triggerType:0) → pre-complete-state │ │ └── (zamanlayici schedule'ini manuel transition ile iptal senaryosu) -│ └── reschedule-timer (Manual, triggerType:0, target: $self) -│ └── (timer'i ayni state'te yeniden zamanlama senaryosu) +│ └── reschedule-timer (Manual, triggerType:0, target: auto-passed-state) +│ └── (aynı state'te self-loop; timer'i yeniden zamanlama — tanim dogrulamasinda `$self` yerine acik state anahtari gerekir) │ ├── timer-triggered-state (Intermediate, stateType:2) │ ├── onEntries: @@ -135,7 +135,7 @@ lifecycle-transitions-test-workflow (F) | Cancel transition | Workflow iptal mekanizmasi | cancel-workflow → terminated-state | | **Exit transition (`attributes.exit`)** | Belirli state'lerde cikis; hedef terminated; IMapping ile exitExecuted | exit-workflow, ExitMapping.csx | | **Schedule iptal (manuel)** | auto-passed-state'ten manuel gecisle timer beklemeden pre-complete | cancel-schedule-manually | -| **Timer reschedule ($self)** | Ayni state'te kalarak zamanlayiciyi yeniden baglama | reschedule-timer → $self | +| **Timer reschedule (self-loop)** | Ayni state'te kalarak zamanlayiciyi yeniden baglama | reschedule-timer → **auto-passed-state** (`$self` semantigi; amorphie-flow E_BAD_TARGET icin acik hedef) | | **queryRoles (workflow)** | authorize endpoint ile workflow datasi sorgu yetkisi | attributes.queryRoles, test-viewer | | **queryRoles (state)** | State bazli allow/deny override | processing-state queryRoles | | **Transition roles** | Manuel transition'da rol grant/deny | move-to-processing, complete-workflow | @@ -151,7 +151,7 @@ lifecycle-transitions-test-workflow (F) | Cancel | cancel-workflow → terminated-state | | **Exit** | initialize-state'ten exit-workflow; terminated-state ve `exitExecuted` | | **Schedule cancel** | auto-passed-state'te cancel-schedule-manually → pre-complete-state | -| **Reschedule** | reschedule-timer ($self), state auto-passed-state kalir | +| **Reschedule** | reschedule-timer (`target: auto-passed-state`), state auto-passed-state kalir | | **QueryRoles / roller** | `/functions/authorize?role=...`; move-to-processing; processing-state'te test-processor / test-viewer deny | ### Kullanilan Bilesenler @@ -1168,7 +1168,7 @@ Her grubun hangi vNext ozelliklerini test ettigini gosteren matris: | Cancel transition | X | X | | | | | | | | | | **Exit transition (`attributes.exit`)** | X | | | | | | | | | | | **Schedule cancel (manuel)** | X | | | | | | | | | | -| **Timer reschedule ($self)** | X | | | | | | | | | | +| **Timer reschedule (self-loop)** | X | | | | | | | | | | | Shared transitions ($self) | | X | | | | | | | | | | **Child-level shared transition** | | X | | | | | | | | | | **SubFlow cancel final states (child/grandchild)** | | X | | | | | | | | | diff --git a/vnext.config.json b/vnext.config.json index 60a37d4..711a424 100644 --- a/vnext.config.json +++ b/vnext.config.json @@ -25,12 +25,7 @@ "description": "Exported components for core domain", "maintainer": "vNext Team", "license": "MIT", - "keywords": [ - "core", - "vnext", - "workflow", - "domain" - ] + "keywords": ["core", "vnext", "workflow", "domain"] } }, "dependencies": { From 3de7c27e7d06b116e53cfa81564dc1eca4d5db07 Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Wed, 29 Apr 2026 16:00:59 +0300 Subject: [PATCH 02/24] lifecycle-transitions-test done --- .../lifecycle-transitions-test-workflow.http | 98 ++- .../postman-lifecycle-transitions.json | 569 +++++++++++++++++- ...cle-transitions-test-workflow.diagram.json | 12 +- .../lifecycle-transitions-test-workflow.json | 10 +- doc/integration-test-documentation.md | 4 +- tests/Core/Helpers/StateFunctionJson.cs | 75 +++ tests/Core/Helpers/TestDataBuilder.cs | 4 +- .../Helpers/WorkflowInstanceTestHelper.cs | 148 +++++ tests/Core/Helpers/WorkflowTestHttpHeaders.cs | 10 + .../LifecycleTransitionsTestWorkflowTests.cs | 386 ++++++++++++ 10 files changed, 1259 insertions(+), 57 deletions(-) create mode 100644 tests/Core/Helpers/StateFunctionJson.cs create mode 100644 tests/Core/Helpers/WorkflowInstanceTestHelper.cs create mode 100644 tests/Core/Helpers/WorkflowTestHttpHeaders.cs create mode 100644 tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs diff --git a/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http b/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http index 9d64069..cd922d8 100644 --- a/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http +++ b/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http @@ -29,8 +29,8 @@ Content-Type: application/json ### Adim 2: Durum Kontrol (initialize-state olmali) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{passInstanceId}}/functions/state -### Adim 3: Manuel Transition - Move to Processing (ITransitionMapping test) -### transition roles: allow sadece test-operator (rolsuz cagri -> instance status F) +### Adim 3: Manuel Transition - Move to Processing (ITransitionMapping + transition roles) +### transition roles: yalnizca test-operator allow. State fonksiyonu (GET .../functions/state) transitions[] listesini role'e gore filtreler (v0.0.39+). PATCH cagrisinda ayni kuralla zorunlu red resmi dokümanda taahhüt edilmez. PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{passInstanceId}}/transitions/move-to-processing?sync=true Content-Type: application/json role: test-operator @@ -112,7 +112,9 @@ role: test-operator {} -### Adim 3: Durum Kontrol (default-fallback-state -> completed-state olmali) +### Adim 3: Durum Kontrol (default-fallback-state gecici, sonra completed-state) +### Not: C# testi WaitForStateAsync(..., 30s) ile completed bekler; tek GET erken default-fallback gosterebilir. +### Gerekirse birkac saniye sonra GET'i tekrarlayin veya state endpoint'ini poll edin. GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{defaultInstanceId}}/functions/state ############################################################################### @@ -187,7 +189,7 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/ ### Adim 1: Workflow Baslat (Exit Test) # @name exitCreate -POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { @@ -202,7 +204,7 @@ Content-Type: application/json @exitInstanceId = {{exitCreate.response.body.$.id}} ### Adim 2: Exit Transition Tetikle (initialize-state'den) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/transitions/exit-workflow +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/transitions/exit-workflow?sync=true Content-Type: application/json {} @@ -215,11 +217,16 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/ ### ============================================================ ### TEST: Schedule Cancel (Manual ile timer iptali) +### xUnit: ScheduleCancel_ManualTransition_CancelsTimerAndReachesPreComplete +### VNext.Testing.Sdk: StartInstanceAsync / RunTransitionAsync cagrilarinda ?sync=true (XML docs). +### Akis: Start -> move-to-processing (role test-operator) -> AssertState auto-passed-state +### -> cancel-schedule-manually (role header YOK) -> AssertState pre-complete-state +### Workflow: auto-passed-state icinde scheduled-timer 10s; iptal timer tetiklenmeden yapilir. ### ============================================================ -### Adim 1: Workflow Baslat (Schedule Cancel Test) +### Adim 1: StartInstanceAsync — key/tags/attributes C# ile ayni # @name scheduleCancelCreate -POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { @@ -234,25 +241,36 @@ Content-Type: application/json ### Response'dan instance ID'yi yakala @scheduleCancelInstanceId = {{scheduleCancelCreate.response.body.$.id}} -### Adim 2: Durum Kontrol - auto-passed-state olmali (auto transitions sonrasi) +### Adim 2: RunTransitionAsync move-to-processing + role (headers null degil) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{scheduleCancelInstanceId}}/transitions/move-to-processing?sync=true +Content-Type: application/json +role: test-operator + +{} + +### Adim 3: GET state — beklenen: auto-passed-state (AssertStateAsync) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{scheduleCancelInstanceId}}/functions/state -### Adim 3: Timer tetiklenmeden cancel-schedule-manually transition cagir -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{scheduleCancelInstanceId}}/transitions/cancel-schedule-manually +### Adim 4: RunTransitionAsync cancel-schedule-manually — role header yok (C# headers: null) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{scheduleCancelInstanceId}}/transitions/cancel-schedule-manually?sync=true Content-Type: application/json {} -### Adim 4: Durum Kontrol - pre-complete-state olmali (timer iptali basarili) +### Adim 5: GET state — beklenen: pre-complete-state (AssertStateAsync; schedule iptali) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{scheduleCancelInstanceId}}/functions/state ### ============================================================ -### TEST: Reschedule Timer ($self transition) +### TEST: Reschedule Timer ($self + yeniden kurulan timer -> pre-complete) +### xUnit: RescheduleTimer_SelfTransition_ThenWaitsForRescheduledTimer_ReachesPreCompleteState +### Akis: move -> auto-passed -> reschedule-timer (yine auto-passed) -> ~10s sonra scheduled transition +### -> timer-triggered-state -> auto-to-pre-complete -> pre-complete-state +### Adim 6 REST Client: reschedule sonrasi en az ~12-15 sn bekleyin (veya GET'i poll ile tekrarlayin). ### ============================================================ ### Adim 1: Workflow Baslat (Reschedule Test) # @name rescheduleCreate -POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { @@ -267,25 +285,61 @@ Content-Type: application/json ### Response'dan instance ID'yi yakala @rescheduleInstanceId = {{rescheduleCreate.response.body.$.id}} -### Adim 2: Durum Kontrol - auto-passed-state olmali +### Adim 2: move-to-processing (test-operator — transition roles zorunlu) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rescheduleInstanceId}}/transitions/move-to-processing?sync=true +Content-Type: application/json +role: test-operator + +{} + +### Adim 3: Durum Kontrol — auto-passed-state (AssertStateAsync) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rescheduleInstanceId}}/functions/state -### Adim 3: Reschedule timer ($self transition - timer sifirlanmali) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rescheduleInstanceId}}/transitions/reschedule-timer +### Adim 4: reschedule-timer ($self; timer yeniden zamanlanmali) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rescheduleInstanceId}}/transitions/reschedule-timer?sync=true Content-Type: application/json {} -### Adim 4: Durum Kontrol - hala auto-passed-state olmali ($self = ayni state) +### Adim 5: Durum Kontrol — hâlâ auto-passed-state (AssertStateAsync, $self) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rescheduleInstanceId}}/functions/state + +### Adim 6: ~12-25 sn SONRA — pre-complete-state (WaitForStateAsync ile es; timer + timer-triggered auto) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rescheduleInstanceId}}/functions/state +############################################################################### +### State function: transitions[] role ile filtrelenir (move-to-processing ornegi, v0.0.39+) +############################################################################### +### Workflow Baslat +# @name stateFilterStart +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "lifecycle-state-filter-{{$timestamp}}", + "tags": ["integration-test", "lifecycle", "state-filter"], + "attributes": { + "testPath": "pass" + } +} + +@stateFilterInstanceId = {{stateFilterStart.response.body.$.id}} + +### initialize-state: GET state + role test-operator — response.transitions icinde move-to-processing beklenir +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{stateFilterInstanceId}}/functions/state +role: test-operator + +### initialize-state: GET state + role test-viewer — transition rollerinde yok; move-to-processing transitions listesinde olmamali +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{stateFilterInstanceId}}/functions/state +role: test-viewer + ### ============================================================ ### TEST: QueryRoles ve Transition Roles ### ============================================================ ### Adim 1: Workflow Baslat (Roles Test) # @name rolesCreate -POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { @@ -301,10 +355,10 @@ Content-Type: application/json @rolesInstanceId = {{rolesCreate.response.body.$.id}} ### Adim 2: QueryRoles ile yetki kontrolu - test-viewer izinli olmali (workflow-level) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/authorize?role=test-viewer +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/authorize?queryRoles=true&role=test-viewer ### Adim 3: move-to-processing transition (test-operator rolu ile) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/transitions/move-to-processing +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/transitions/move-to-processing?sync=true Content-Type: application/json role: test-operator @@ -314,7 +368,7 @@ role: test-operator GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/state ### Adim 5: QueryRoles - processing-state'de test-processor izinli, test-viewer reddedilmis olmali -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/authorize?role=test-processor +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/authorize?queryRoles=true&role=test-processor ### Adim 6: QueryRoles - test-viewer deny olmali (state-level override) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/authorize?role=test-viewer +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/authorize?queryRoles=true&role=test-viewer diff --git a/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json b/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json index 4f30506..a827d9b 100644 --- a/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json +++ b/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json @@ -847,7 +847,7 @@ } ], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": [ "{{baseUrl}}" ], @@ -859,6 +859,12 @@ "{{workflow}}", "instances", "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } ] }, "body": { @@ -900,7 +906,7 @@ } ], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/transitions/exit-workflow", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/transitions/exit-workflow?sync=true", "host": [ "{{baseUrl}}" ], @@ -914,6 +920,12 @@ "{{exitInstanceId}}", "transitions", "exit-workflow" + ], + "query": [ + { + "key": "sync", + "value": "true" + } ] }, "body": { @@ -980,6 +992,7 @@ }, { "name": "TEST: Schedule Cancel (Manual ile timer iptali)", + "description": "xUnit: ScheduleCancel_ManualTransition_CancelsTimerAndReachesPreComplete. VNext.Testing.Sdk tum start/transition cagrilarinda sync=true ekler. Akis: start -> move-to-processing (role: test-operator) -> state=auto-passed -> cancel-schedule-manually (rol header yok) -> state=pre-complete. Zamanlayici 10s; iptal bunun onunde yapilir.", "item": [ { "name": "Adim 1: Workflow Baslat (Schedule Cancel Test)", @@ -993,7 +1006,7 @@ } ], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": [ "{{baseUrl}}" ], @@ -1005,6 +1018,12 @@ "{{workflow}}", "instances", "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } ] }, "body": { @@ -1024,18 +1043,88 @@ "script": { "type": "text/javascript", "exec": [ + "pm.test('ScheduleCancel: start 200 (sync=true, StartInstanceAsync)', function () {", + " pm.response.to.have.status(200);", + "});", "var jsonData = pm.response.json();", "if (jsonData && jsonData.id) {", " pm.collectionVariables.set(\"scheduleCancelInstanceId\", jsonData.id);", " console.log(\"scheduleCancelInstanceId set to: \" + jsonData.id);", - "}" + "}", + "pm.test('ScheduleCancel: start yanıtında id', function () {", + " pm.expect(jsonData && jsonData.id).to.be.ok;", + "});" ] } } ] }, { - "name": "Adim 2: Durum Kontrol - auto-passed-state olmali (auto transitions sonrasi)", + "name": "Adim 2: move-to-processing (RunTransitionAsync + role test-operator)", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "role", + "value": "test-operator", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{scheduleCancelInstanceId}}/transitions/move-to-processing?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{scheduleCancelInstanceId}}", + "transitions", + "move-to-processing" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('ScheduleCancel: move-to-processing 200 (sync=true)', function () {", + " pm.response.to.have.status(200);", + "});" + ] + } + } + ] + }, + { + "name": "Adim 3: Durum Kontrol - auto-passed-state (AssertStateAsync)", "request": { "method": "GET", "header": [], @@ -1057,10 +1146,31 @@ ] } }, - "response": [] + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "function extractStateName(body) {", + " if (!body || typeof body !== 'object') { return ''; }", + " if (typeof body.state === 'string') { return body.state; }", + " if (body.data && typeof body.data === 'object' && typeof body.data.state === 'string') { return body.data.state; }", + " return '';", + "}", + "var j = pm.response.json();", + "var name = extractStateName(j);", + "pm.test('ScheduleCancel: state auto-passed-state (C# ScheduleCancel satir 287)', function () {", + " pm.expect(name).to.equal('auto-passed-state');", + "});" + ] + } + } + ] }, { - "name": "Adim 3: Timer tetiklenmeden cancel-schedule-manually transition cagir", + "name": "Adim 4: cancel-schedule-manually (rol yok; RunTransitionAsync headers null)", "request": { "method": "PATCH", "header": [ @@ -1071,7 +1181,7 @@ } ], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{scheduleCancelInstanceId}}/transitions/cancel-schedule-manually", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{scheduleCancelInstanceId}}/transitions/cancel-schedule-manually?sync=true", "host": [ "{{baseUrl}}" ], @@ -1085,6 +1195,12 @@ "{{scheduleCancelInstanceId}}", "transitions", "cancel-schedule-manually" + ], + "query": [ + { + "key": "sync", + "value": "true" + } ] }, "body": { @@ -1097,10 +1213,23 @@ } } }, - "response": [] + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('ScheduleCancel: cancel-schedule-manually 200 (sync=true)', function () {", + " pm.response.to.have.status(200);", + "});" + ] + } + } + ] }, { - "name": "Adim 4: Durum Kontrol - pre-complete-state olmali (timer iptali basarili)", + "name": "Adim 5: Durum Kontrol - pre-complete-state (AssertStateAsync; timer iptal)", "request": { "method": "GET", "header": [], @@ -1122,12 +1251,34 @@ ] } }, - "response": [] + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "function extractStatePre(body) {", + " if (!body || typeof body !== 'object') { return ''; }", + " if (typeof body.state === 'string') { return body.state; }", + " if (body.data && typeof body.data === 'object' && typeof body.data.state === 'string') { return body.data.state; }", + " return '';", + "}", + "var j = pm.response.json();", + "var name = extractStatePre(j);", + "pm.test('ScheduleCancel: state pre-complete-state (C# ScheduleCancel satir 291)', function () {", + " pm.expect(name).to.equal('pre-complete-state');", + "});" + ] + } + } + ] } ] }, { - "name": "TEST: Reschedule Timer ($self transition)", + "name": "TEST: Reschedule Timer ($self + yeniden kurulan timer -> pre-complete)", + "description": "xUnit: RescheduleTimer_SelfTransition_ThenWaitsForRescheduledTimer_ReachesPreCompleteState. reschedule-timer sonrasi auto-passed kali, sonra yeniden kurulan ~10s timer -> timer-triggered-state -> auto -> pre-complete. Adim 5b postman-echo 12s beklemesi icerir (offline ise Runner delay veya manuel bekleyip 5b atlayin; dogrudan Adim 6 GET ile poll edin).", "item": [ { "name": "Adim 1: Workflow Baslat (Reschedule Test)", @@ -1141,7 +1292,7 @@ } ], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": [ "{{baseUrl}}" ], @@ -1153,6 +1304,12 @@ "{{workflow}}", "instances", "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } ] }, "body": { @@ -1183,7 +1340,58 @@ ] }, { - "name": "Adim 2: Durum Kontrol - auto-passed-state olmali", + "name": "Adim 2: move-to-processing (test-operator — transition roles)", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "role", + "value": "test-operator", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rescheduleInstanceId}}/transitions/move-to-processing?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{rescheduleInstanceId}}", + "transitions", + "move-to-processing" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [] + }, + { + "name": "Adim 3: Durum Kontrol - auto-passed-state (AssertStateAsync)", "request": { "method": "GET", "header": [], @@ -1205,10 +1413,31 @@ ] } }, - "response": [] + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "function extractStateName(body) {", + " if (!body || typeof body !== 'object') { return ''; }", + " if (typeof body.state === 'string') { return body.state; }", + " if (body.data && typeof body.data === 'object' && typeof body.data.state === 'string') { return body.data.state; }", + " return '';", + "}", + "var j = pm.response.json();", + "var name = extractStateName(j);", + "pm.test('Reschedule: once move sonrasi auto-passed-state', function () {", + " pm.expect(name).to.equal('auto-passed-state');", + "});" + ] + } + } + ] }, { - "name": "Adim 3: Reschedule timer ($self transition - timer sifirlanmali)", + "name": "Adim 4: reschedule-timer ($self, timer reschedule)", "request": { "method": "PATCH", "header": [ @@ -1219,7 +1448,7 @@ } ], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rescheduleInstanceId}}/transitions/reschedule-timer", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rescheduleInstanceId}}/transitions/reschedule-timer?sync=true", "host": [ "{{baseUrl}}" ], @@ -1233,6 +1462,12 @@ "{{rescheduleInstanceId}}", "transitions", "reschedule-timer" + ], + "query": [ + { + "key": "sync", + "value": "true" + } ] }, "body": { @@ -1245,10 +1480,23 @@ } } }, - "response": [] + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('Reschedule: reschedule-timer 200', function () {", + " pm.response.to.have.status(200);", + "});" + ] + } + } + ] }, { - "name": "Adim 4: Durum Kontrol - hala auto-passed-state olmali ($self = ayni state)", + "name": "Adim 5: Durum Kontrol - hala auto-passed-state ($self)", "request": { "method": "GET", "header": [], @@ -1270,7 +1518,256 @@ ] } }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "function extractStateName2(body) {", + " if (!body || typeof body !== 'object') { return ''; }", + " if (typeof body.state === 'string') { return body.state; }", + " if (body.data && typeof body.data === 'object' && typeof body.data.state === 'string') { return body.data.state; }", + " return '';", + "}", + "var j = pm.response.json();", + "var name = extractStateName2(j);", + "pm.test('Reschedule: reschedule sonrasi hala auto-passed-state', function () {", + " pm.expect(name).to.equal('auto-passed-state');", + "});" + ] + } + } + ] + }, + { + "name": "Adim 5b: Bekleme ~12s (yeniden kurulan timer; postman-echo — opsiyonel)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "https://postman-echo.com/delay/12", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "delay", + "12" + ] + } + }, "response": [] + }, + { + "name": "Adim 6: Durum Kontrol - pre-complete-state (rescheduled timer tamamlandi)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rescheduleInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{rescheduleInstanceId}}", + "functions", + "state" + ] + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "function extractStatePre(body) {", + " if (!body || typeof body !== 'object') { return ''; }", + " if (typeof body.state === 'string') { return body.state; }", + " if (body.data && typeof body.data === 'object' && typeof body.data.state === 'string') { return body.data.state; }", + " return '';", + "}", + "var j = pm.response.json();", + "var name = extractStatePre(j);", + "pm.test('Reschedule: pre-complete-state (timer-triggered + auto)', function () {", + " pm.expect(name).to.equal('pre-complete-state');", + "});" + ] + } + } + ] + } + ] + }, + { + "name": "TEST: State — transitions filtrelenir (role, v0.0.39+)", + "item": [ + { + "name": "Workflow Baslat (state filter)", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"lifecycle-state-filter-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"lifecycle\", \"state-filter\"],\n \"attributes\": {\n \"testPath\": \"pass\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "if (jsonData && jsonData.id) {", + " pm.collectionVariables.set(\"stateFilterInstanceId\", jsonData.id);", + " console.log(\"stateFilterInstanceId set to: \" + jsonData.id);", + "}" + ] + } + } + ] + }, + { + "name": "GET state + role test-operator — transitions icinde move-to-processing", + "request": { + "method": "GET", + "header": [ + { + "key": "role", + "value": "test-operator", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{stateFilterInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{stateFilterInstanceId}}", + "functions", + "state" + ] + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var j = pm.response.json();", + "var tr = j.transitions || [];", + "var hasMove = tr.some(function (x) {", + " return x.name === 'move-to-processing' || x.key === 'move-to-processing';", + "});", + "pm.test('transitions icinde move-to-processing (test-operator)', function () {", + " pm.expect(hasMove).to.be.true;", + "});" + ] + } + } + ] + }, + { + "name": "GET state + role test-viewer — move-to-processing listelenmez", + "request": { + "method": "GET", + "header": [ + { + "key": "role", + "value": "test-viewer", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{stateFilterInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{stateFilterInstanceId}}", + "functions", + "state" + ] + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var j = pm.response.json();", + "var tr = j.transitions || [];", + "var hasMove = tr.some(function (x) {", + " return x.name === 'move-to-processing' || x.key === 'move-to-processing';", + "});", + "pm.test('move-to-processing yok (test-viewer, transition role disinda)', function () {", + " pm.expect(hasMove).to.be.false;", + "});" + ] + } + } + ] } ] }, @@ -1289,7 +1786,7 @@ } ], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": [ "{{baseUrl}}" ], @@ -1301,6 +1798,12 @@ "{{workflow}}", "instances", "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } ] }, "body": { @@ -1336,7 +1839,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/authorize?role=test-viewer", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/authorize?queryRoles=true&role=test-viewer", "host": [ "{{baseUrl}}" ], @@ -1352,6 +1855,10 @@ "authorize" ], "query": [ + { + "key": "queryRoles", + "value": "true" + }, { "key": "role", "value": "test-viewer" @@ -1378,7 +1885,7 @@ } ], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/transitions/move-to-processing", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/transitions/move-to-processing?sync=true", "host": [ "{{baseUrl}}" ], @@ -1392,6 +1899,12 @@ "{{rolesInstanceId}}", "transitions", "move-to-processing" + ], + "query": [ + { + "key": "sync", + "value": "true" + } ] }, "body": { @@ -1437,7 +1950,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/authorize?role=test-processor", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/authorize?queryRoles=true&role=test-processor", "host": [ "{{baseUrl}}" ], @@ -1453,6 +1966,10 @@ "authorize" ], "query": [ + { + "key": "queryRoles", + "value": "true" + }, { "key": "role", "value": "test-processor" @@ -1468,7 +1985,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/authorize?role=test-viewer", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/authorize?queryRoles=true&role=test-viewer", "host": [ "{{baseUrl}}" ], @@ -1484,6 +2001,10 @@ "authorize" ], "query": [ + { + "key": "queryRoles", + "value": "true" + }, { "key": "role", "value": "test-viewer" diff --git a/core/Workflows/lifecycle-transitions/.meta/lifecycle-transitions-test-workflow.diagram.json b/core/Workflows/lifecycle-transitions/.meta/lifecycle-transitions-test-workflow.diagram.json index 1dcccf8..37f1e34 100644 --- a/core/Workflows/lifecycle-transitions/.meta/lifecycle-transitions-test-workflow.diagram.json +++ b/core/Workflows/lifecycle-transitions/.meta/lifecycle-transitions-test-workflow.diagram.json @@ -1,16 +1,16 @@ { "nodePos": { "initialize-state": { - "x": 440, - "y": 300 + "x": 480, + "y": 380 }, "processing-state": { - "x": 860, - "y": 120 + "x": 840, + "y": 40 }, "auto-passed-state": { - "x": 1340, - "y": -80 + "x": 1500, + "y": -140 }, "timer-triggered-state": { "x": 1640, diff --git a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json index 5879e5f..c86d428 100644 --- a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json +++ b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json @@ -3,7 +3,7 @@ "flow": "sys-flows", "flowVersion": "1.0.0", "domain": "core", - "version": "1.4.0", + "version": "1.5.0", "tags": [ "integration-test", "lifecycle", @@ -56,8 +56,8 @@ "target": "terminated-state", "triggerType": 0, "versionStrategy": "Major", - "availableIn": [], "onExecutionTasks": [], + "availableIn": [], "labels": [ { "language": "en-US", @@ -172,6 +172,12 @@ "label": "Isleme Gec (Manuel + ITransitionMapping)" } ], + "roles": [ + { + "role": "test-operator", + "grant": "allow" + } + ], "schema": null, "view": null, "mapping": { diff --git a/doc/integration-test-documentation.md b/doc/integration-test-documentation.md index a15734c..c40fd69 100644 --- a/doc/integration-test-documentation.md +++ b/doc/integration-test-documentation.md @@ -135,7 +135,7 @@ lifecycle-transitions-test-workflow (F) | Cancel transition | Workflow iptal mekanizmasi | cancel-workflow → terminated-state | | **Exit transition (`attributes.exit`)** | Belirli state'lerde cikis; hedef terminated; IMapping ile exitExecuted | exit-workflow, ExitMapping.csx | | **Schedule iptal (manuel)** | auto-passed-state'ten manuel gecisle timer beklemeden pre-complete | cancel-schedule-manually | -| **Timer reschedule (self-loop)** | Ayni state'te kalarak zamanlayiciyi yeniden baglama | reschedule-timer → **auto-passed-state** (`$self` semantigi; amorphie-flow E_BAD_TARGET icin acik hedef) | +| **Timer reschedule (self-loop)** | Ayni state'e manuel gecis ile zamanlayici reschedule | reschedule-timer → **auto-passed-state**; sonra yeniden kurulan `scheduled-timer-transition` (~10s) → timer-triggered → auto → pre-complete | | **queryRoles (workflow)** | authorize endpoint ile workflow datasi sorgu yetkisi | attributes.queryRoles, test-viewer | | **queryRoles (state)** | State bazli allow/deny override | processing-state queryRoles | | **Transition roles** | Manuel transition'da rol grant/deny | move-to-processing, complete-workflow | @@ -151,7 +151,7 @@ lifecycle-transitions-test-workflow (F) | Cancel | cancel-workflow → terminated-state | | **Exit** | initialize-state'ten exit-workflow; terminated-state ve `exitExecuted` | | **Schedule cancel** | auto-passed-state'te cancel-schedule-manually → pre-complete-state | -| **Reschedule** | reschedule-timer (`target: auto-passed-state`), state auto-passed-state kalir | +| **Reschedule** | reschedule-timer ile kisa sure `auto-passed-state`; yeniden kurulan ~10s timer sonrasi `timer-triggered-state` → auto → **pre-complete-state** (xUnit: `RescheduleTimer_SelfTransition_ThenWaitsForRescheduledTimer_ReachesPreCompleteState`) | | **QueryRoles / roller** | `/functions/authorize?role=...`; move-to-processing; processing-state'te test-processor / test-viewer deny | ### Kullanilan Bilesenler diff --git a/tests/Core/Helpers/StateFunctionJson.cs b/tests/Core/Helpers/StateFunctionJson.cs new file mode 100644 index 0000000..1a1f8a5 --- /dev/null +++ b/tests/Core/Helpers/StateFunctionJson.cs @@ -0,0 +1,75 @@ +using System.Text.Json; + +namespace Core.IntegrationTests.Helpers; + +/// +/// Parses GET .../functions/state JSON for tests. Shape may evolve; extend with new branches if needed. +/// State cevabında geçişler genelde transitions[].name ile listelenir; bkz. . +/// +public static class StateFunctionJson +{ + /// + /// Resolves current state key/name from a State function response body. + /// + public static string ExtractStateName(JsonElement body) + { + if ( + body.ValueKind == JsonValueKind.Object + && body.TryGetProperty("state", out var direct) + && direct.ValueKind == JsonValueKind.String + ) + return direct.GetString() ?? string.Empty; + + if ( + body.ValueKind == JsonValueKind.Object + && body.TryGetProperty("data", out var data) + && data.ValueKind == JsonValueKind.Object + && data.TryGetProperty("state", out var nested) + && nested.ValueKind == JsonValueKind.String + ) + return nested.GetString() ?? string.Empty; + + return string.Empty; + } + + /// + /// Whether .transitions[] contains an item whose name equals (primary for runtime state function), or whose key equals it if present. + /// + public static bool TransitionsContainKey(JsonElement stateBody, string transitionKey) + { + if ( + stateBody.ValueKind != JsonValueKind.Object + || !stateBody.TryGetProperty("transitions", out var transitions) + ) + return false; + + if (transitions.ValueKind != JsonValueKind.Array) + return false; + + foreach (var t in transitions.EnumerateArray()) + { + if (t.ValueKind != JsonValueKind.Object) + continue; + + if ( + t.TryGetProperty("name", out var nameEl) + && nameEl.ValueKind == JsonValueKind.String + ) + { + if (string.Equals(nameEl.GetString(), transitionKey, StringComparison.Ordinal)) + return true; + } + + if ( + t.TryGetProperty("key", out var keyEl) + && keyEl.ValueKind == JsonValueKind.String + ) + { + if (string.Equals(keyEl.GetString(), transitionKey, StringComparison.Ordinal)) + return true; + } + } + + return false; + } +} diff --git a/tests/Core/Helpers/TestDataBuilder.cs b/tests/Core/Helpers/TestDataBuilder.cs index 4ea9e37..1b092a0 100644 --- a/tests/Core/Helpers/TestDataBuilder.cs +++ b/tests/Core/Helpers/TestDataBuilder.cs @@ -4,7 +4,9 @@ namespace Core.IntegrationTests.Helpers; /// /// Core-specific factory methods for building vNext workflow instance payloads. -/// Add static methods here for each workflow your domain tests. +/// +/// Instance/transition API orchestration helpers: , +/// , . /// /// Inherited helpers from : /// - BuildInstancePayload(key, tags, attributes) diff --git a/tests/Core/Helpers/WorkflowInstanceTestHelper.cs b/tests/Core/Helpers/WorkflowInstanceTestHelper.cs new file mode 100644 index 0000000..fd27dc0 --- /dev/null +++ b/tests/Core/Helpers/WorkflowInstanceTestHelper.cs @@ -0,0 +1,148 @@ +using System.Net; +using System.Text.Json; +using VNext.Testing.Sdk.Client; +using Xunit; + +namespace Core.IntegrationTests.Helpers; + +/// +/// Reusable helpers for workflow instance API flows (start, state, transitions, authorize) against a single workflow key. +/// +public sealed class WorkflowInstanceTestHelper +{ + private readonly VNextApiClient _api; + private readonly string _workflowKey; + private readonly string _domain; + private readonly string _apiVersion; + + public WorkflowInstanceTestHelper( + VNextApiClient api, + string workflowKey, + string domain = "core", + string apiVersion = "1" + ) + { + _api = api; + _workflowKey = workflowKey; + _domain = domain; + _apiVersion = apiVersion; + } + + /// URL-segment safe unique instance key. + public static string UniqueInstanceKey(string prefix) => $"{prefix}-{Guid.NewGuid():N}"; + + public async Task StartInstanceIdAsync( + object body, + Dictionary? headers = null + ) + { + var response = await _api.StartInstanceAsync(_workflowKey, body, headers); + Assert.True( + response.Body.TryGetProperty("id", out var idEl), + "Start response should contain id" + ); + var id = idEl.GetString(); + Assert.False(string.IsNullOrWhiteSpace(id)); + return id!; + } + + public Task RunTransitionAsync( + string instanceId, + string transitionKey, + Dictionary? headers, + object? transitionBody = null + ) => + _api.RunTransitionAsync( + _workflowKey, + instanceId, + transitionKey, + transitionBody ?? new { }, + headers + ); + + public async Task GetStateNameAsync(string instanceId) + { + var response = await _api.CallInstanceFunctionAsync( + _workflowKey, + instanceId, + "state", + null, + null + ); + + return StateFunctionJson.ExtractStateName(response.Body); + } + + public async Task GetStateFunctionBodyAsync( + string instanceId, + Dictionary? headers + ) + { + var response = await _api.CallInstanceFunctionAsync( + _workflowKey, + instanceId, + "state", + null, + headers + ); + return response.Body; + } + + public async Task AssertStateAsync(string instanceId, string expected) + { + var name = await GetStateNameAsync(instanceId); + Assert.Equal(expected, name); + } + + public async Task WaitForStateAsync(string instanceId, string expected, TimeSpan timeout) + { + var deadline = DateTime.UtcNow + timeout; + while (DateTime.UtcNow < deadline) + { + if (await GetStateNameAsync(instanceId) == expected) + return; + await Task.Delay(200); + } + + var current = await GetStateNameAsync(instanceId); + Assert.Fail($"Timeout waiting for state '{expected}'. Current: '{current}'"); + } + + /// + /// Polls state until one of matches, or timeout; returns last observed state name. + /// + public async Task PollStateUntilAnyAsync( + string instanceId, + TimeSpan window, + IReadOnlySet stopOnAny, + int pollIntervalMs = 20 + ) + { + var deadline = DateTime.UtcNow + window; + while (DateTime.UtcNow < deadline) + { + var s = await GetStateNameAsync(instanceId); + if (stopOnAny.Contains(s)) + return s; + await Task.Delay(pollIntervalMs); + } + + return await GetStateNameAsync(instanceId); + } + + public async Task AssertAuthorizeQueryRolesAsync( + string instanceId, + string role, + bool expectAllowed + ) + { + var path = + $"/api/v{_apiVersion}/{_domain}/workflows/{Uri.EscapeDataString(_workflowKey)}/instances/{Uri.EscapeDataString(instanceId)}/functions/authorize?queryRoles=true&role={Uri.EscapeDataString(role)}"; + + var response = await _api.GetRawAsync(path); + if (expectAllowed) + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + else + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } +} diff --git a/tests/Core/Helpers/WorkflowTestHttpHeaders.cs b/tests/Core/Helpers/WorkflowTestHttpHeaders.cs new file mode 100644 index 0000000..1804138 --- /dev/null +++ b/tests/Core/Helpers/WorkflowTestHttpHeaders.cs @@ -0,0 +1,10 @@ +namespace Core.IntegrationTests.Helpers; + +/// +/// Common HTTP header maps for vNext Runtime integration tests (e.g. role for state filtering / transitions). +/// +public static class WorkflowTestHttpHeaders +{ + public static Dictionary? Role(string role) => + string.IsNullOrEmpty(role) ? null : new Dictionary { ["role"] = role }; +} diff --git a/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs b/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs new file mode 100644 index 0000000..2a45ddb --- /dev/null +++ b/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs @@ -0,0 +1,386 @@ +using System.Text.Json; +using Core.IntegrationTests.Helpers; +using Core.IntegrationTests.Infrastructure; + +namespace Core.IntegrationTests.Tests.LifecycleTransitionsTestWorkflow; + +/* +* Bug olabilecek noktalar +* - cancel: availableIn: [] içerisine wf cli'ı yazılan state'leri error olarak +* algılıyor. Runtime tarafı nasıl bilemiyorum +* - bir state'ten başka bir state'e transtion çalıştırıldığında +* diğer state'in auto transition'u varsa ve bu otomatik çalışınca +* başka state'e geçiyorsa ve son geçilen state'in hatalı transition mapping code'u +* varsa status F oluyor ve son state geçişi gösterilmiyor. Ne demek istiyorum: +* A -> B -> C -> D +* A'dan B'ye manuel geçiş. B auto geçişi var. C'ye geçiyor problem yok gibi. +* C'nin D'ye transition'u var ve bu transition'a bağlı mapping'de problem var. +* workflow deployunda felan problem çıkmyıor. A'dan B'ye transition olduğunda +* state B'de gösteriliyor ama aslında C ile D arasındaki transition'dan dolayı uygulama +* F status'e düşüyor. workflow B durumunda F status ile duruyor kalıyor. +! Kısacası feedback yanlış oluyor. state consistency yanlış oluyor. +* +*/ + +/* +! Buglar: +! Cancel +! Exit +! Reschedule (reschedule-timer sonrasi scheduled transition yeniden kurulmuyor; instance auto-passed-state'te kaliyor) +*/ + +/// +/// Integration tests aligned with api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http. +/// Helpers live in . +/// +public class LifecycleTransitionsTestWorkflowTests : IntegrationTestBase +{ + private const string WorkflowKey = "lifecycle-transitions-test-workflow"; + + private readonly WorkflowInstanceTestHelper _workflow; + + public LifecycleTransitionsTestWorkflowTests(VNextTestEnvironment environment) + : base(environment) + { + _workflow = new WorkflowInstanceTestHelper(Api, WorkflowKey); + } + + [Fact] + public async Task HappyPath_Pass_ReachesCompletedState() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("lifecycle-pass-test"); + var instanceId = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "integration-test", "lifecycle", "pass-path" }, + attributes = new { testPath = "pass" }, + } + ); + + await _workflow.AssertStateAsync(instanceId, "initialize-state"); + + await _workflow.RunTransitionAsync( + instanceId, + "move-to-processing", + WorkflowTestHttpHeaders.Role("test-operator") + ); + + await _workflow.AssertStateAsync(instanceId, "auto-passed-state"); + + await _workflow.WaitForStateAsync( + instanceId, + "pre-complete-state", + TimeSpan.FromSeconds(15) + ); + + await _workflow.RunTransitionAsync( + instanceId, + "complete-workflow", + WorkflowTestHttpHeaders.Role("test-approver") + ); + + await _workflow.AssertStateAsync(instanceId, "completed-state"); + } + + [Fact] + public async Task FailPath_ReachesAutoFailedState() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("lifecycle-fail-test"); + var instanceId = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "integration-test", "lifecycle", "fail-path" }, + attributes = new { testPath = "fail" }, + } + ); + + await _workflow.RunTransitionAsync( + instanceId, + "move-to-processing", + WorkflowTestHttpHeaders.Role("test-operator") + ); + + await _workflow.AssertStateAsync(instanceId, "auto-failed-state"); + } + + [Fact] + public async Task DefaultAutoPath_UnknownTestPath_ReachesCompletedState() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("lifecycle-default-test"); + var instanceId = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "integration-test", "lifecycle", "default-path" }, + attributes = new { testPath = "unknown" }, + } + ); + + await _workflow.RunTransitionAsync( + instanceId, + "move-to-processing", + WorkflowTestHttpHeaders.Role("test-operator") + ); + + await _workflow.WaitForStateAsync(instanceId, "completed-state", TimeSpan.FromSeconds(30)); + } + + [Fact] + public async Task IdempotentStart_SameKey_ReturnsSameInstanceId() + { + var key = $"lifecycle-idempotent-{Guid.NewGuid():N}"; + var id1 = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "integration-test", "lifecycle", "idempotent" }, + attributes = new { testPath = "pass" }, + } + ); + + var id2 = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "integration-test" }, + attributes = new { testPath = "pass" }, + } + ); + + Assert.Equal(id1, id2); + } + + // TODO: Bu test şu anda başarısız — runtime/platform tarafında cancel ile ilgili bug var; workflow tanımı doğru kabul ediliyor. + [Fact] + public async Task CancelTransition_ReachesTerminatedState() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("lifecycle-cancel-test"); + var instanceId = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "integration-test", "lifecycle", "cancel" }, + attributes = new { testPath = "pass" }, + } + ); + + await _workflow.RunTransitionAsync(instanceId, "cancel-workflow", headers: null); + + await _workflow.AssertStateAsync(instanceId, "terminated-state"); + } + + // TODO: Bu test şu anda başarısız — runtime/platform tarafında exit ile ilgili bug var; workflow tanımı doğru kabul ediliyor. + [Fact] + public async Task ExitTransition_FromInitialize_SetsExitExecutedOnInstance() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("exit-test"); + var instanceId = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "exit-test" }, + attributes = new { testId = "exit-test" }, + } + ); + + await _workflow.RunTransitionAsync(instanceId, "exit-workflow", headers: null); + + await _workflow.AssertStateAsync(instanceId, "terminated-state"); + + var instanceResponse = await Api.GetInstanceAsync(WorkflowKey, instanceId); + var body = instanceResponse.Body; + + Assert.True( + body.TryGetProperty("attributes", out var attributes), + "GetInstance response should include attributes" + ); + + Assert.True( + attributes.TryGetProperty("exitExecuted", out var exitExecuted) + && exitExecuted.ValueKind == JsonValueKind.True, + "Exit mapping should set attributes.exitExecuted = true" + ); + } + + [Fact] + public async Task ScheduleCancel_ManualTransition_CancelsTimerAndReachesPreComplete() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("schedule-cancel-test"); + var instanceId = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "schedule-cancel-test" }, + attributes = new { testId = "schedule-cancel-test", testPath = "pass" }, + } + ); + + await _workflow.RunTransitionAsync( + instanceId, + "move-to-processing", + WorkflowTestHttpHeaders.Role("test-operator") + ); + + await _workflow.AssertStateAsync(instanceId, "auto-passed-state"); + + await _workflow.RunTransitionAsync(instanceId, "cancel-schedule-manually", headers: null); + + await _workflow.AssertStateAsync(instanceId, "pre-complete-state"); + } + + // TODO / BUG: reschedule-timer çalışınca workflow tanımına göre scheduled-timer-transition yeniden kurulmalı (~10s sonra timer-triggered → pre-complete). + // Şu an runtime’da timer yeniden schedule olmuyor; instance sürekli auto-passed-state’te kalıyor — WaitForStateAsync(pre-complete) zaman aşımına düşer. + // Workflow JSON / reschedule-timer → auto-passed-state ($self) doğru kabul ediliyor; platform tarafı incelenmeli. + /// + /// reschedule-timer: $self ile hâlâ auto-passed-state; ardından zamanlayıcı yeniden kurulmalı (ShortTimerMapping +10s). + /// Beklenen: scheduled-timer-transition -> timer-triggered-state -> auto-to-pre-complete -> pre-complete-state. + /// + [Fact] + public async Task RescheduleTimer_SelfTransition_ThenWaitsForRescheduledTimer_ReachesPreCompleteState() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("reschedule-test"); + var instanceId = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "reschedule-test" }, + attributes = new { testId = "reschedule-test", testPath = "pass" }, + } + ); + + await _workflow.RunTransitionAsync( + instanceId, + "move-to-processing", + WorkflowTestHttpHeaders.Role("test-operator") + ); + + await _workflow.AssertStateAsync(instanceId, "auto-passed-state"); + + await _workflow.RunTransitionAsync(instanceId, "reschedule-timer", headers: null); + + await _workflow.AssertStateAsync(instanceId, "auto-passed-state"); + + // Yeniden kurulan timer ~10 sn; timer-triggered onEntries + auto gecis icin pay + await _workflow.WaitForStateAsync( + instanceId, + "pre-complete-state", + TimeSpan.FromSeconds(20) + ); + } + + [Fact] + public async Task QueryRoles_WorkflowLevel_AllowsTestViewerOnInitialize() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("roles-test"); + var instanceId = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "roles-test" }, + attributes = new { testId = "roles-test", testPath = "pass" }, + } + ); + + await _workflow.AssertAuthorizeQueryRolesAsync( + instanceId, + "test-viewer", + expectAllowed: true + ); + } + + [Fact] + public async Task QueryRoles_OnProcessingState_AllowsProcessor_DeniesViewerWhenObserved() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("roles-processing-test"); + var instanceId = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "roles-test" }, + attributes = new { testId = "roles-test", testPath = "pass" }, + } + ); + + await _workflow.RunTransitionAsync( + instanceId, + "move-to-processing", + WorkflowTestHttpHeaders.Role("test-operator") + ); + + var observed = await _workflow.PollStateUntilAnyAsync( + instanceId, + TimeSpan.FromSeconds(2), + new HashSet(StringComparer.Ordinal) { "processing-state", "auto-passed-state" } + ); + + if (observed == "processing-state") + { + await _workflow.AssertAuthorizeQueryRolesAsync( + instanceId, + "test-processor", + expectAllowed: true + ); + await _workflow.AssertAuthorizeQueryRolesAsync( + instanceId, + "test-viewer", + expectAllowed: false + ); + } + else + { + Assert.Equal("auto-passed-state", observed); + } + } + + [Fact] + public async Task StateFunction_WithTestOperatorRole_IncludesMoveToProcessingInTransitions() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("lifecycle-state-filter-allow"); + var instanceId = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "integration-test", "lifecycle", "state-filter" }, + attributes = new { testPath = "pass" }, + } + ); + + await _workflow.AssertStateAsync(instanceId, "initialize-state"); + + var body = await _workflow.GetStateFunctionBodyAsync( + instanceId, + WorkflowTestHttpHeaders.Role("test-operator") + ); + Assert.True( + StateFunctionJson.TransitionsContainKey(body, "move-to-processing"), + "State response transitions[] should include an item with name \"move-to-processing\" when role matches transition roles (v0.0.39+ filtering)." + ); + } + + [Fact] + public async Task StateFunction_WithTestViewerRole_ExcludesMoveToProcessingFromTransitions() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("lifecycle-state-filter-deny"); + var instanceId = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "integration-test", "lifecycle", "state-filter" }, + attributes = new { testPath = "pass" }, + } + ); + + await _workflow.AssertStateAsync(instanceId, "initialize-state"); + + var body = await _workflow.GetStateFunctionBodyAsync( + instanceId, + WorkflowTestHttpHeaders.Role("test-viewer") + ); + Assert.False( + StateFunctionJson.TransitionsContainKey(body, "move-to-processing"), + "State response transitions[] should not list name \"move-to-processing\" when caller role is not in transition roles allow list." + ); + } +} From f3186a640a8f5098edb31bdcc9610da3ca288971 Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Wed, 29 Apr 2026 16:44:28 +0300 Subject: [PATCH 03/24] =?UTF-8?q?In=20addition=20to=20the=20existing=20tes?= =?UTF-8?q?ts,=20coverage=20was=20added=20for=20persisting=20data=20enrich?= =?UTF-8?q?ment=20(derived=20from=20on-entry=20task=20results)=20into=20th?= =?UTF-8?q?e=20state=20body.=20An=20extra=20test=20was=20also=20added=20fo?= =?UTF-8?q?r=20another=20state=20referenced=20under=20the=20exit=20transit?= =?UTF-8?q?ion=E2=80=99s=20onAvailable=20field?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lifecycle-transitions-test-workflow.http | 17 ++- .../postman-lifecycle-transitions.json | 119 ++++++++++++++- .../lifecycle-transitions-test-workflow.json | 20 +-- doc/integration-test-documentation.md | 28 ++-- tests/Core/Helpers/JsonElementAssertions.cs | 49 ++++++ ...LifecyclePassPathInstanceDataAssertions.cs | 63 ++++++++ .../LifecycleTransitionsTestWorkflowTests.cs | 139 ++++++++++++++---- 7 files changed, 381 insertions(+), 54 deletions(-) create mode 100644 tests/Core/Helpers/JsonElementAssertions.cs create mode 100644 tests/Core/Tests/lifecycle-transitions-test-workflow/LifecyclePassPathInstanceDataAssertions.cs diff --git a/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http b/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http index cd922d8..8747789 100644 --- a/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http +++ b/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http @@ -335,6 +335,8 @@ role: test-viewer ### ============================================================ ### TEST: QueryRoles ve Transition Roles +### State-level queryRoles artık pre-complete-state üzerinde (processing-state auto geçişli, gözlemlenemez). +### Akış: move -> auto-passed -> cancel-schedule-manually -> pre-complete -> authorize (processor OK, viewer deny) ### ============================================================ ### Adim 1: Workflow Baslat (Roles Test) @@ -364,11 +366,20 @@ role: test-operator {} -### Adim 4: Durum Kontrol - processing-state olmali +### Adim 4: Durum Kontrol - auto-passed-state olmali (sync sonrasi processing atlanir) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/state -### Adim 5: QueryRoles - processing-state'de test-processor izinli, test-viewer reddedilmis olmali +### Adim 5: cancel-schedule-manually (timer beklemeden pre-complete — Schedule cancel ile ayni gecis) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/transitions/cancel-schedule-manually?sync=true +Content-Type: application/json + +{} + +### Adim 6: Durum Kontrol - pre-complete-state olmali +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/state + +### Adim 7: QueryRoles - pre-complete-state'te test-processor izinli olmali GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/authorize?queryRoles=true&role=test-processor -### Adim 6: QueryRoles - test-viewer deny olmali (state-level override) +### Adim 8: QueryRoles - test-viewer deny olmali (state-level override) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/authorize?queryRoles=true&role=test-viewer diff --git a/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json b/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json index a827d9b..563a268 100644 --- a/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json +++ b/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json @@ -1611,6 +1611,7 @@ }, { "name": "TEST: State — transitions filtrelenir (role, v0.0.39+)", + "description": "Yalnizca initialize-state'te GET /functions/state: transition roles ile transitions[] filtrelenir. move-to-processing sonrasi processing-state genelde auto ile atlanir; bu klasorde move sonrasi durum assert'i yoktur.", "item": [ { "name": "Workflow Baslat (state filter)", @@ -1773,6 +1774,7 @@ }, { "name": "TEST: QueryRoles ve Transition Roles", + "description": "State-level queryRoles pre-complete-state uzerindedir (processing-state auto gecisli). Akis: move -> auto-passed -> cancel-schedule-manually -> pre-complete -> authorize test-processor OK, test-viewer 403.", "item": [ { "name": "Adim 1: Workflow Baslat (Roles Test)", @@ -1920,7 +1922,7 @@ "response": [] }, { - "name": "Adim 4: Durum Kontrol - processing-state olmali", + "name": "Adim 4: Durum Kontrol - auto-passed-state (sync sonrasi)", "request": { "method": "GET", "header": [], @@ -1942,10 +1944,121 @@ ] } }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "function sn(b) {", + " if (!b || typeof b !== 'object') return '';", + " if (typeof b.state === 'string') return b.state;", + " if (b.data && typeof b.data.state === 'string') return b.data.state;", + " return '';", + "}", + "var n = sn(pm.response.json());", + "pm.test('Roles flow: auto-passed-state', function () {", + " pm.expect(n).to.equal('auto-passed-state');", + "});" + ] + } + } + ] + }, + { + "name": "Adim 5: cancel-schedule-manually (pre-complete'e git)", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/transitions/cancel-schedule-manually?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{rolesInstanceId}}", + "transitions", + "cancel-schedule-manually" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [] }, { - "name": "Adim 5: QueryRoles - processing-state'de test-processor izinli, test-viewer reddedilmis olmali", + "name": "Adim 6: Durum Kontrol - pre-complete-state", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rolesInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{rolesInstanceId}}", + "functions", + "state" + ] + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "function sn(b) {", + " if (!b || typeof b !== 'object') return '';", + " if (typeof b.state === 'string') return b.state;", + " if (b.data && typeof b.data.state === 'string') return b.data.state;", + " return '';", + "}", + "var n = sn(pm.response.json());", + "pm.test('Roles flow: pre-complete-state', function () {", + " pm.expect(n).to.equal('pre-complete-state');", + "});" + ] + } + } + ] + }, + { + "name": "Adim 7: QueryRoles - pre-complete'te test-processor izinli", "request": { "method": "GET", "header": [], @@ -1980,7 +2093,7 @@ "response": [] }, { - "name": "Adim 6: QueryRoles - test-viewer deny olmali (state-level override)", + "name": "Adim 8: QueryRoles - test-viewer deny (state-level)", "request": { "method": "GET", "header": [], diff --git a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json index c86d428..12d0f2b 100644 --- a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json +++ b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json @@ -203,16 +203,6 @@ "label": "Isleme Durumu (Ara Durum, onEntries + onExits)" } ], - "queryRoles": [ - { - "role": "test-processor", - "grant": "allow" - }, - { - "role": "test-viewer", - "grant": "deny" - } - ], "view": null, "subFlow": null, "onEntries": [ @@ -459,6 +449,16 @@ "label": "Tamamlama Oncesi Durum" } ], + "queryRoles": [ + { + "role": "test-processor", + "grant": "allow" + }, + { + "role": "test-viewer", + "grant": "deny" + } + ], "view": null, "subFlow": null, "onEntries": [], diff --git a/doc/integration-test-documentation.md b/doc/integration-test-documentation.md index c40fd69..66e9ea3 100644 --- a/doc/integration-test-documentation.md +++ b/doc/integration-test-documentation.md @@ -8,7 +8,7 @@ Bu dokuman, `core` projesindeki tum integration test workflow'larini, icerdikler | # | Grup | Workflow | Test Edilen Ana Ozellikler | |---|------|----------|---------------------------| -| 1 | Lifecycle & Transitions | `lifecycle-transitions-test-workflow` | State tipleri, transition tipleri, on-entries/exits, timer, cancel, **exit** (`attributes.exit`), **schedule iptal / timer reschedule**, **queryRoles** ve **transition rolleri** | +| 1 | Lifecycle & Transitions | `lifecycle-transitions-test-workflow` | State tipleri, transition tipleri, on-entries/exits, timer, cancel, **exit** (`attributes.exit`), **schedule iptal / timer reschedule**, **queryRoles** ve **transition `roles` (yalnizca state fonksiyonu listeleme filtresi)** | | 2 | SubFlow & SubProcess | `subflow-orchestration-parent` + child + grandchild | Parent-child-grandchild zinciri, parent shared transitions, **child shared transition**, **cancel cascade** (child/grandchild **cancelled** final state'leri), **effective state**, **updateData (SubFlow)** | | 3 | Task Execution | `task-execution-test-workflow` + `task-target-workflow` | HTTP/Script/StartFlow/GetInstanceData task tipleri | | 4 | Error Boundary | `error-boundary-test-workflow` | Retry, Ignore, **Rollback**, **Log**, **Notify** (action:4) aksiyonlari, retry policy, priority, **timeoutPolicy** (onTimeout), **errorHandlerRule** (errorTypes/errorCodes) | @@ -42,7 +42,11 @@ core/ **Workflow:** `lifecycle-transitions-test-workflow` (type: F) -**Amac:** Workflow yasam dongusu ve tum transition tiplerini test eder; ayrica **exit transition**, **zamanlayici schedule yonetimi** (manuel iptal ve **self-loop reschedule** — hedef olarak mevcut state anahtari `auto-passed-state`; `$self` ile ayni davranis), **workflow/state queryRoles** ve **transition bazli roller** senaryolarini kapsar. +**Amac:** Workflow yasam dongusu ve tum transition tiplerini test eder; ayrica **exit transition**, **zamanlayici schedule yonetimi** (manuel iptal ve **self-loop reschedule** — hedef olarak mevcut state anahtari `auto-passed-state`; `$self` ile ayni davranis), **workflow/state queryRoles** ve **`GET .../functions/state` icinde transition listesinin role gore filtrelenmesi** senaryolarini kapsar. + +**Onemli (transition `roles`):** Runtime modelinde transition uzerindeki `roles` (allow/deny), **PATCH ile gecisi calistirmayi engelleyen bir yetki kapisi degildir**; agirlikli olarak **state fonksiyonu yanitindaki `transitions[]` listesini** cagiran rol header'ina gore **suzmek** icin kullanilir. Rolü izin listesinde olmayan bir istemci, UI/API listesinde o gecisi **gormeyebilir**; bu, dokumante edilen davranista gecisin API ile **mutlaka reddedilecegi** anlamina gelmez. (Ayrı mekanizma: **`authorize?queryRoles=...`** ile workflow/instance datasi sorgu yetkisi.) + +**C# testleri:** `StateFunction_*` testleri, ilgili **role header** ile state sorgulandiginda `transitions[]` icinde beklenen anahtarin **listelenip listelenmedigini** dogrular. ### Agac Yapisi @@ -68,10 +72,9 @@ lifecycle-transitions-test-workflow (F) │ └── Transitions: │ └── move-to-processing (Manual, triggerType:0) → processing-state │ ├── mapping: ProcessTransitionMapping.csx (ITransitionMapping) -│ └── roles: test-operator (allow) +│ └── roles: test-operator (allow) — state `transitions[]` filtresi; PATCH zorunlu red degil │ ├── processing-state (Intermediate, stateType:2) -│ ├── queryRoles: test-processor (allow), test-viewer (deny) │ ├── onEntries: │ │ └── script-task + ProcessEntryMapping.csx │ ├── onExits: @@ -101,9 +104,10 @@ lifecycle-transitions-test-workflow (F) │ └── rule: AlwaysTrueRule.csx │ ├── pre-complete-state (Intermediate, stateType:2) +│ ├── queryRoles: test-processor (allow), test-viewer (deny) │ └── Transitions: │ └── complete-workflow (Manual, triggerType:0) → completed-state -│ └── roles: test-approver (allow), test-operator (deny) +│ └── roles: test-approver (allow), test-operator (deny) — listeleme filtresi; deny PATCH'i garanti etmez │ ├── default-fallback-state (Intermediate, stateType:2) │ └── Transitions: @@ -137,8 +141,8 @@ lifecycle-transitions-test-workflow (F) | **Schedule iptal (manuel)** | auto-passed-state'ten manuel gecisle timer beklemeden pre-complete | cancel-schedule-manually | | **Timer reschedule (self-loop)** | Ayni state'e manuel gecis ile zamanlayici reschedule | reschedule-timer → **auto-passed-state**; sonra yeniden kurulan `scheduled-timer-transition` (~10s) → timer-triggered → auto → pre-complete | | **queryRoles (workflow)** | authorize endpoint ile workflow datasi sorgu yetkisi | attributes.queryRoles, test-viewer | -| **queryRoles (state)** | State bazli allow/deny override | processing-state queryRoles | -| **Transition roles** | Manuel transition'da rol grant/deny | move-to-processing, complete-workflow | +| **queryRoles (state)** | State bazli allow/deny override | **pre-complete-state** queryRoles (`processing-state` auto gecisli; testte cancel-schedule ile pre-complete) | +| **Transition `roles`** | **`GET .../functions/state` yanıtında `transitions[]` listesini** role gore **filtreleme** (v0.0.39+); transition yürütme (PATCH) için aynı listedeki deny'in zorunlu red anlamı **yoktur** | move-to-processing, complete-workflow; xUnit: `StateFunction_WithTestOperatorRole_*` / `StateFunction_WithTestViewerRole_*` | | Version strategy (Major/Minor/Patch) | Farkli versiyonlama stratejileri | Cesitli transitions | | Idempotent start | Ayni key ile tekrar baslatma | HTTP test dosyasinda | @@ -152,7 +156,7 @@ lifecycle-transitions-test-workflow (F) | **Exit** | initialize-state'ten exit-workflow; terminated-state ve `exitExecuted` | | **Schedule cancel** | auto-passed-state'te cancel-schedule-manually → pre-complete-state | | **Reschedule** | reschedule-timer ile kisa sure `auto-passed-state`; yeniden kurulan ~10s timer sonrasi `timer-triggered-state` → auto → **pre-complete-state** (xUnit: `RescheduleTimer_SelfTransition_ThenWaitsForRescheduledTimer_ReachesPreCompleteState`) | -| **QueryRoles / roller** | `/functions/authorize?role=...`; move-to-processing; processing-state'te test-processor / test-viewer deny | +| **QueryRoles / transition listesi** | **authorize:** `/functions/authorize?queryRoles=true&role=...` (datasi sorgu). **State listesi:** `GET .../functions/state` + role header ile **move-to-processing** gorunurlugu (allow/omit). **pre-complete-state:** cancel-schedule ile; queryRoles'ta test-processor allow / test-viewer deny | ### Kullanilan Bilesenler @@ -629,6 +633,8 @@ view-function-extension-test-workflow (F) **Amac:** Master schema, transition schema validasyonu, updateData ($self) mekanizmasi ve field roles ozelliklerini test eder. +**Not (field roles vs. transition roles):** Bu gruptaki **field roles**, master şema uzerinde **alan bazlı gorunurluk** (or. `GET .../functions/data` ciktisinda rol ile alan suzme) ile ilgilidir. **Grup 1** `lifecycle-transitions` workflow'undaki transition **`roles`** alani ise **`GET .../functions/state` icindeki `transitions[]` listeleme filtresi** icindir; ikisi farkli mekanizmalardir. + ### Agac Yapisi ``` @@ -676,7 +682,7 @@ schema-data-test-workflow (F) | Schema validasyonu | Gecerli data ile transition | confirm-with-schema | | Schema sessiz reddi | Gecersiz data gonderildiginde state degismez | HTTP test: eksik required alan gonderme | | updateData ($self) | State degistirmeden data guncelleme | update-instance-data | -| Field roles | Alan bazli erisim kontrolu | internalNote (admin/customer), auditLog (auditor) | +| Field roles (master schema alan gorunurlugu) | Alan bazli data gorunurlugu | internalNote (admin/customer), auditLog (auditor) | | JSON Schema Draft 2020-12 | Schema standardi uyumu | Her iki schema dosyasi | | Required alan kontrolu | Zorunlu alanlarin validasyonu | orderId, customerName, amount, currency | | Enum validasyonu | Gecerli deger kümesi kontrolu | currency: TRY, USD, EUR | @@ -1177,9 +1183,9 @@ Her grubun hangi vNext ozelliklerini test ettigini gosteren matris: | **UpdateData (SubFlow context)** | | X | | | | | | | | | | Master schema | | | | | | X | | | | | | Transition schema | | | | | | X | | | | | -| Field roles | | | | | | X | | | | | +| Field roles (master schema) | | | | | | X | | | | | | **queryRoles (workflow/state)** | X | | | | | | | | | | -| **Transition roles** | X | | | | | | | | | | +| **Transition roles (state fn. list filter)** | X | | | | | | | | | | | Error boundary (task) | | | | X | | | | | | | | Error boundary (state) | | | | X | | | | | | | | Error boundary (workflow) | | | | X | | | | | | | diff --git a/tests/Core/Helpers/JsonElementAssertions.cs b/tests/Core/Helpers/JsonElementAssertions.cs new file mode 100644 index 0000000..5cc9cf0 --- /dev/null +++ b/tests/Core/Helpers/JsonElementAssertions.cs @@ -0,0 +1,49 @@ +using System.Text.Json; +using Xunit; + +namespace Core.IntegrationTests.Helpers; + +/// +/// üzerinde sık kullanılan test assertion'ları (ör. GetInstance attributes). +/// +public static class JsonElementAssertions +{ + public static void AssertPropertyTrue( + JsonElement parent, + string name, + string? message = null + ) + { + Assert.True( + parent.TryGetProperty(name, out var el) && el.ValueKind == JsonValueKind.True, + message ?? $"Property '{name}' should be JSON true." + ); + } + + public static void AssertPropertyString( + JsonElement parent, + string name, + string expected, + string? message = null + ) + { + Assert.True( + parent.TryGetProperty(name, out var el) && el.GetString() == expected, + message ?? $"Property '{name}' should equal '{expected}'." + ); + } + + public static void AssertPropertyNonEmptyString( + JsonElement parent, + string name, + string? message = null + ) + { + Assert.True( + parent.TryGetProperty(name, out var el) + && el.ValueKind == JsonValueKind.String + && !string.IsNullOrWhiteSpace(el.GetString()), + message ?? $"Property '{name}' should be a non-empty string." + ); + } +} diff --git a/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecyclePassPathInstanceDataAssertions.cs b/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecyclePassPathInstanceDataAssertions.cs new file mode 100644 index 0000000..a5e666e --- /dev/null +++ b/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecyclePassPathInstanceDataAssertions.cs @@ -0,0 +1,63 @@ +using System.Linq; +using System.Text.Json; +using Core.IntegrationTests.Helpers; +using Xunit; + +namespace Core.IntegrationTests.Tests.LifecycleTransitionsTestWorkflow; + +/// +/// lifecycle-transitions-test-workflow pass path (timer ile pre-complete-state) için instance +/// attributes sözleşmesi. Alan adları core/Workflows/lifecycle-transitions/src/*.csx ile hizalıdır. +/// +internal static class LifecyclePassPathInstanceDataAssertions +{ + private const string CsxContractHint = + "(lifecycle-transitions *.csx onEntry / onExit / transition mapping contract)"; + + public static void AssertAfterPreCompleteViaTimer(JsonElement attributes) + { + JsonElementAssertions.AssertPropertyTrue( + attributes, + "initialized", + $"initialized should be true {CsxContractHint}" + ); + JsonElementAssertions.AssertPropertyNonEmptyString(attributes, "initializedAt"); + JsonElementAssertions.AssertPropertyString(attributes, "testPath", "pass"); + + Assert.True( + attributes.TryGetProperty("stepLog", out var stepLog) + && stepLog.ValueKind == JsonValueKind.Array, + "stepLog should be a JSON array (InitializeDataMapping)" + ); + var steps = stepLog.EnumerateArray().Select(e => e.GetString()).ToArray(); + Assert.Contains("initialize-state:onEntry", steps); + + JsonElementAssertions.AssertPropertyTrue( + attributes, + "transitionMappingExecuted", + $"transitionMappingExecuted should be true {CsxContractHint}" + ); + JsonElementAssertions.AssertPropertyNonEmptyString(attributes, "transitionMappingAt"); + + JsonElementAssertions.AssertPropertyTrue( + attributes, + "processEntryExecuted", + $"processEntryExecuted should be true {CsxContractHint}" + ); + JsonElementAssertions.AssertPropertyNonEmptyString(attributes, "processedAt"); + + JsonElementAssertions.AssertPropertyTrue( + attributes, + "processExitExecuted", + $"processExitExecuted should be true {CsxContractHint}" + ); + JsonElementAssertions.AssertPropertyNonEmptyString(attributes, "exitedAt"); + + JsonElementAssertions.AssertPropertyTrue( + attributes, + "timerTriggered", + $"timerTriggered should be true {CsxContractHint}" + ); + JsonElementAssertions.AssertPropertyNonEmptyString(attributes, "timerTriggeredAt"); + } +} diff --git a/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs b/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs index 2a45ddb..6a29323 100644 --- a/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs +++ b/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs @@ -31,7 +31,8 @@ namespace Core.IntegrationTests.Tests.LifecycleTransitionsTestWorkflow; /// /// Integration tests aligned with api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http. -/// Helpers live in . +/// Paylaşılan API yardımcıları: ; bu workflow’a özel veri sözleşmesi: +/// . /// public class LifecycleTransitionsTestWorkflowTests : IntegrationTestBase { @@ -83,6 +84,52 @@ await _workflow.RunTransitionAsync( await _workflow.AssertStateAsync(instanceId, "completed-state"); } + /// + /// Timer ile pre-complete-state'e ulaşan pass path'te instance verisinde, ilgili state'lerde tanımlı + /// onEntry / onExit (ve geçiş) script görevlerinin instance data'ya yazdığı alanların beklenen şekilde + /// dolduğunu doğrular. Böylece bu görevlerin fiilen çalıştığı veri sözleşmesi üzerinden garanti altına alınır. + /// + /// + /// Alan adları core/Workflows/lifecycle-transitions/src/*.csx ile eşleşir; script gövdeleri değişirse bu test bilinçli olarak güncellenmelidir. + /// + [Fact] + public async Task PassPath_InstanceData_AfterPreCompleteViaTimer_ReflectsOnEntryOnExitScripts() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("lifecycle-pass-data-contract"); + var instanceId = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "integration-test", "lifecycle", "pass-data-contract" }, + attributes = new { testPath = "pass" }, + } + ); + + await _workflow.AssertStateAsync(instanceId, "initialize-state"); + + await _workflow.RunTransitionAsync( + instanceId, + "move-to-processing", + WorkflowTestHttpHeaders.Role("test-operator") + ); + + await _workflow.AssertStateAsync(instanceId, "auto-passed-state"); + + await _workflow.WaitForStateAsync( + instanceId, + "pre-complete-state", + TimeSpan.FromSeconds(15) + ); + + var instanceAtPreComplete = await Api.GetInstanceAsync(WorkflowKey, instanceId); + Assert.True( + instanceAtPreComplete.Body.TryGetProperty("attributes", out var attrs), + "GetInstance should include attributes" + ); + + LifecyclePassPathInstanceDataAssertions.AssertAfterPreCompleteViaTimer(attrs); + } + [Fact] public async Task FailPath_ReachesAutoFailedState() { @@ -204,6 +251,51 @@ public async Task ExitTransition_FromInitialize_SetsExitExecutedOnInstance() ); } + // TODO: Bu test şu anda başarısız — runtime/platform tarafında exit ile ilgili bug var; workflow tanımı doğru kabul ediliyor. + [Fact] + public async Task ExitTransition_FromPreComplete_SetsExitExecutedOnInstance() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("exit-precomplete-test"); + var instanceId = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "exit-precomplete-test" }, + attributes = new { testId = "exit-precomplete-test", testPath = "pass" }, + } + ); + + await _workflow.RunTransitionAsync( + instanceId, + "move-to-processing", + WorkflowTestHttpHeaders.Role("test-operator") + ); + + await _workflow.AssertStateAsync(instanceId, "auto-passed-state"); + + await _workflow.RunTransitionAsync(instanceId, "cancel-schedule-manually", headers: null); + + await _workflow.AssertStateAsync(instanceId, "pre-complete-state"); + + await _workflow.RunTransitionAsync(instanceId, "exit-workflow", headers: null); + + await _workflow.AssertStateAsync(instanceId, "terminated-state"); + + var instanceResponse = await Api.GetInstanceAsync(WorkflowKey, instanceId); + var body = instanceResponse.Body; + + Assert.True( + body.TryGetProperty("attributes", out var attributes), + "GetInstance response should include attributes" + ); + + Assert.True( + attributes.TryGetProperty("exitExecuted", out var exitExecuted) + && exitExecuted.ValueKind == JsonValueKind.True, + "Exit mapping should set attributes.exitExecuted = true" + ); + } + [Fact] public async Task ScheduleCancel_ManualTransition_CancelsTimerAndReachesPreComplete() { @@ -291,9 +383,9 @@ await _workflow.AssertAuthorizeQueryRolesAsync( } [Fact] - public async Task QueryRoles_OnProcessingState_AllowsProcessor_DeniesViewerWhenObserved() + public async Task QueryRoles_OnPreCompleteState_AllowsProcessor_DeniesViewer() { - var key = WorkflowInstanceTestHelper.UniqueInstanceKey("roles-processing-test"); + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("roles-precomplete-test"); var instanceId = await _workflow.StartInstanceIdAsync( new { @@ -309,29 +401,22 @@ await _workflow.RunTransitionAsync( WorkflowTestHttpHeaders.Role("test-operator") ); - var observed = await _workflow.PollStateUntilAnyAsync( + await _workflow.AssertStateAsync(instanceId, "auto-passed-state"); + + await _workflow.RunTransitionAsync(instanceId, "cancel-schedule-manually", headers: null); + + await _workflow.AssertStateAsync(instanceId, "pre-complete-state"); + + await _workflow.AssertAuthorizeQueryRolesAsync( + instanceId, + "test-processor", + expectAllowed: true + ); + await _workflow.AssertAuthorizeQueryRolesAsync( instanceId, - TimeSpan.FromSeconds(2), - new HashSet(StringComparer.Ordinal) { "processing-state", "auto-passed-state" } - ); - - if (observed == "processing-state") - { - await _workflow.AssertAuthorizeQueryRolesAsync( - instanceId, - "test-processor", - expectAllowed: true - ); - await _workflow.AssertAuthorizeQueryRolesAsync( - instanceId, - "test-viewer", - expectAllowed: false - ); - } - else - { - Assert.Equal("auto-passed-state", observed); - } + "test-viewer", + expectAllowed: false + ); } [Fact] @@ -355,7 +440,7 @@ public async Task StateFunction_WithTestOperatorRole_IncludesMoveToProcessingInT ); Assert.True( StateFunctionJson.TransitionsContainKey(body, "move-to-processing"), - "State response transitions[] should include an item with name \"move-to-processing\" when role matches transition roles (v0.0.39+ filtering)." + "State response transitions[] should list \"move-to-processing\" when caller role is in transition allow list (v0.0.39+ list filtering only; PATCH is not role-gated by this list)." ); } @@ -380,7 +465,7 @@ public async Task StateFunction_WithTestViewerRole_ExcludesMoveToProcessingFromT ); Assert.False( StateFunctionJson.TransitionsContainKey(body, "move-to-processing"), - "State response transitions[] should not list name \"move-to-processing\" when caller role is not in transition roles allow list." + "State response transitions[] should omit \"move-to-processing\" when caller role is not in allow list (UI/list filtering); same transition may still be invokable via PATCH depending on runtime/API policy." ); } } From 4b85c50f524bc9db9a81cb0328fe37edf5766ec7 Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Thu, 30 Apr 2026 12:53:35 +0300 Subject: [PATCH 04/24] task types unified under the task-execution logical groups so extended task logical group removed. --- .../extended-tasks-test-workflow.http | 59 -- .../postman-extended-tasks.json | 221 ------ .../extended-tasks-test-workflow.http | 40 ++ .../postman-extended-tasks.json | 144 ++++ .../postman-task-execution.json | 96 ++- .../task-execution-test-workflow.http | 31 +- core.link.json | 5 + .../dapr-binding-task.json | 2 +- .../dapr-http-task.json | 2 +- .../dapr-pubsub-task.json | 2 +- .../dapr-service-task.json | 2 +- .../get-instance-data-task.json | 0 .../get-instances-task.json | 2 +- .../http-process-task.json | 0 .../human-task.json | 2 +- .../notification-task.json | 2 +- .../start-flow-task.json | 0 .../subprocess-task.json | 2 +- .../task-exec-script-task.json | 0 .../trigger-transition-task.json | 2 +- .../extended-tasks-test-workflow.json | 644 ------------------ .../src/GetInstancesMapping.csx | 47 -- .../extended-tasks/src/HumanTaskMapping.csx | 42 -- .../src/NotificationMapping.csx | 41 -- .../extended-tasks/src/SubProcessMapping.csx | 41 -- .../src/TriggerTransitionMapping.csx | 41 -- ...cle-transitions-test-workflow.diagram.json | 24 +- .../task-execution-test-workflow.diagram.json | 48 +- .../extended-tasks-test-workflow.json | 358 ++++++++++ .../task-execution/src/AlwaysTrueRule.csx | 11 - .../DaprBindingMapping.csx | 0 .../DaprHttpMapping.csx | 0 .../DaprPubSubMapping.csx | 0 .../DaprServiceMapping.csx | 0 .../InitExtendedTaskMapping.csx | 0 .../src/shared}/AlwaysTrueRule.csx | 0 .../CrossWorkflowMapping.csx | 0 .../GetInstanceDataMapping.csx | 0 .../GetInstancesMapping.csx | 59 ++ .../HttpProcessMapping.csx | 0 .../HumanTaskMapping.csx | 53 ++ .../InitTaskTestMapping.csx | 0 .../NotificationMapping.csx | 53 ++ .../ScriptProcessMapping.csx | 0 .../StartFlowMapping.csx | 0 .../SubProcessMapping.csx | 53 ++ .../TriggerTransitionMapping.csx | 53 ++ .../task-execution-test-workflow.json | 316 ++++++++- .../task-execution/task-target-workflow.json | 2 +- doc/integration-test-documentation.md | 458 +++++-------- index.js | 45 +- .../LifecycleTransitionsTestWorkflowTests.cs | 18 - 52 files changed, 1502 insertions(+), 1519 deletions(-) delete mode 100644 api-tests/extended-tasks/extended-tasks-test-workflow.http delete mode 100644 api-tests/extended-tasks/postman-extended-tasks.json create mode 100644 api-tests/task-execution/extended-tasks-test-workflow.http create mode 100644 api-tests/task-execution/postman-extended-tasks.json create mode 100644 core.link.json rename core/Tasks/{extended-tasks => task-execution/extended-tasks-test-workflow}/dapr-binding-task.json (79%) rename core/Tasks/{extended-tasks => task-execution/extended-tasks-test-workflow}/dapr-http-task.json (81%) rename core/Tasks/{extended-tasks => task-execution/extended-tasks-test-workflow}/dapr-pubsub-task.json (79%) rename core/Tasks/{extended-tasks => task-execution/extended-tasks-test-workflow}/dapr-service-task.json (81%) rename core/Tasks/task-execution/{ => task-execution-test-workflow}/get-instance-data-task.json (100%) rename core/Tasks/{extended-tasks => task-execution/task-execution-test-workflow}/get-instances-task.json (81%) rename core/Tasks/task-execution/{ => task-execution-test-workflow}/http-process-task.json (100%) rename core/Tasks/{extended-tasks => task-execution/task-execution-test-workflow}/human-task.json (82%) rename core/Tasks/{extended-tasks => task-execution/task-execution-test-workflow}/notification-task.json (80%) rename core/Tasks/task-execution/{ => task-execution-test-workflow}/start-flow-task.json (100%) rename core/Tasks/{extended-tasks => task-execution/task-execution-test-workflow}/subprocess-task.json (79%) rename core/Tasks/task-execution/{ => task-execution-test-workflow}/task-exec-script-task.json (100%) rename core/Tasks/{extended-tasks => task-execution/task-execution-test-workflow}/trigger-transition-task.json (82%) delete mode 100644 core/Workflows/extended-tasks/extended-tasks-test-workflow.json delete mode 100644 core/Workflows/extended-tasks/src/GetInstancesMapping.csx delete mode 100644 core/Workflows/extended-tasks/src/HumanTaskMapping.csx delete mode 100644 core/Workflows/extended-tasks/src/NotificationMapping.csx delete mode 100644 core/Workflows/extended-tasks/src/SubProcessMapping.csx delete mode 100644 core/Workflows/extended-tasks/src/TriggerTransitionMapping.csx create mode 100644 core/Workflows/task-execution/extended-tasks-test-workflow.json delete mode 100644 core/Workflows/task-execution/src/AlwaysTrueRule.csx rename core/Workflows/{extended-tasks/src => task-execution/src/extended-tasks-test-workflow}/DaprBindingMapping.csx (100%) rename core/Workflows/{extended-tasks/src => task-execution/src/extended-tasks-test-workflow}/DaprHttpMapping.csx (100%) rename core/Workflows/{extended-tasks/src => task-execution/src/extended-tasks-test-workflow}/DaprPubSubMapping.csx (100%) rename core/Workflows/{extended-tasks/src => task-execution/src/extended-tasks-test-workflow}/DaprServiceMapping.csx (100%) rename core/Workflows/{extended-tasks/src => task-execution/src/extended-tasks-test-workflow}/InitExtendedTaskMapping.csx (100%) rename core/Workflows/{extended-tasks/src => task-execution/src/shared}/AlwaysTrueRule.csx (100%) rename core/Workflows/task-execution/src/{ => task-execution-test-workflow}/CrossWorkflowMapping.csx (100%) rename core/Workflows/task-execution/src/{ => task-execution-test-workflow}/GetInstanceDataMapping.csx (100%) create mode 100644 core/Workflows/task-execution/src/task-execution-test-workflow/GetInstancesMapping.csx rename core/Workflows/task-execution/src/{ => task-execution-test-workflow}/HttpProcessMapping.csx (100%) create mode 100644 core/Workflows/task-execution/src/task-execution-test-workflow/HumanTaskMapping.csx rename core/Workflows/task-execution/src/{ => task-execution-test-workflow}/InitTaskTestMapping.csx (100%) create mode 100644 core/Workflows/task-execution/src/task-execution-test-workflow/NotificationMapping.csx rename core/Workflows/task-execution/src/{ => task-execution-test-workflow}/ScriptProcessMapping.csx (100%) rename core/Workflows/task-execution/src/{ => task-execution-test-workflow}/StartFlowMapping.csx (100%) create mode 100644 core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx create mode 100644 core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx diff --git a/api-tests/extended-tasks/extended-tasks-test-workflow.http b/api-tests/extended-tasks/extended-tasks-test-workflow.http deleted file mode 100644 index 80e1f4c..0000000 --- a/api-tests/extended-tasks/extended-tasks-test-workflow.http +++ /dev/null @@ -1,59 +0,0 @@ -@baseUrl = http://localhost:4201 -@apiVersion = 1 -@domain = core -@workflow = extended-tasks-test-workflow - -### ============================================================ -### TEST 1: Happy Path - Tum Task Tipleri Zinciri -### Dapr HTTP (1) → Dapr Service (3) → Dapr Binding (2) → -### Dapr PubSub (4) → Notification (10) → TriggerTransition (12) → -### SubProcess (14) → GetInstances (15) → Human Task (5) → Completed -### ============================================================ - -### Adim 1: Workflow Baslat -# @name createInstance -POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start -Content-Type: application/json - -{ - "key": "extended-tasks-test-{{$timestamp}}", - "tags": ["extended-tasks-test"], - "attributes": { - "testId": "extended-tasks-happy-path" - } -} - -### Response'dan instance ID'yi yakala -@instanceId = {{createInstance.response.body.$.id}} - -### Adim 2: Durum Kontrol - Auto zincir sonrasi human-task-state'de beklenmeli -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state - -### Adim 3: Instance Data Kontrol - Tum task sonuclarini kontrol et -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}} - -### Adim 4: Human Task Onayla (manuel transition) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/approve-human-task -Content-Type: application/json - -{} - -### Adim 5: Durum Kontrol - completed-state olmali -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state - -### Adim 6: Final Instance Data - Tum task sonuclari mevcut olmali -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}} - -### ============================================================ -### Beklenen Sonuclar: -### - initCompleted: true -### - taskResults.daprHttp.completed: true -### - taskResults.daprService.completed: true -### - taskResults.daprBinding.completed: true -### - taskResults.daprPubSub.completed: true -### - taskResults.notification.completed: true -### - taskResults.triggerTransition.completed: true -### - taskResults.subprocess.completed: true -### - taskResults.getInstances.completed: true -### - taskResults.humanTask.completed: true -### ============================================================ diff --git a/api-tests/extended-tasks/postman-extended-tasks.json b/api-tests/extended-tasks/postman-extended-tasks.json deleted file mode 100644 index 5e00926..0000000 --- a/api-tests/extended-tasks/postman-extended-tasks.json +++ /dev/null @@ -1,221 +0,0 @@ -{ - "info": { - "_postman_id": "382174e0-fc37-4d36-ba7b-653b40194f3c", - "name": "vNext Test - Extended Tasks", - "description": "Auto-generated from extended-tasks-test-workflow.http.\nvNext workflow integration test collection for extended-tasks.", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "variable": [ - { - "key": "baseUrl", - "value": "http://localhost:4201", - "type": "string" - }, - { - "key": "apiVersion", - "value": "1", - "type": "string" - }, - { - "key": "domain", - "value": "core", - "type": "string" - }, - { - "key": "workflow", - "value": "extended-tasks-test-workflow", - "type": "string" - } - ], - "item": [ - { - "name": "Adim 1: Workflow Baslat", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "start" - ] - }, - "body": { - "mode": "raw", - "raw": "{\n \"key\": \"extended-tasks-test-{{$timestamp}}\",\n \"tags\": [\"extended-tasks-test\"],\n \"attributes\": {\n \"testId\": \"extended-tasks-happy-path\"\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - } - }, - "response": [], - "event": [ - { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "if (jsonData && jsonData.id) {", - " pm.collectionVariables.set(\"instanceId\", jsonData.id);", - " console.log(\"instanceId set to: \" + jsonData.id);", - "}" - ] - } - } - ] - }, - { - "name": "Adim 2: Durum Kontrol - Auto zincir sonrasi human-task-state'de beklenmeli", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceId}}", - "functions", - "state" - ] - } - }, - "response": [] - }, - { - "name": "Adim 3: Instance Data Kontrol - Tum task sonuclarini kontrol et", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceId}}" - ] - } - }, - "response": [] - }, - { - "name": "Adim 4: Human Task Onayla (manuel transition)", - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/approve-human-task", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceId}}", - "transitions", - "approve-human-task" - ] - }, - "body": { - "mode": "raw", - "raw": "{}", - "options": { - "raw": { - "language": "json" - } - } - } - }, - "response": [] - }, - { - "name": "Adim 5: Durum Kontrol - completed-state olmali", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceId}}", - "functions", - "state" - ] - } - }, - "response": [] - }, - { - "name": "Adim 6: Final Instance Data - Tum task sonuclari mevcut olmali", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceId}}" - ] - } - }, - "response": [] - } - ] -} \ No newline at end of file diff --git a/api-tests/task-execution/extended-tasks-test-workflow.http b/api-tests/task-execution/extended-tasks-test-workflow.http new file mode 100644 index 0000000..02368ae --- /dev/null +++ b/api-tests/task-execution/extended-tasks-test-workflow.http @@ -0,0 +1,40 @@ +@baseUrl = http://localhost:4201 +@apiVersion = 1 +@domain = core +@workflow = extended-tasks-test-workflow + +### ============================================================ +### TEST: Dapr task zinciri (sadece dis servis / Dapr) +### Dapr HTTP (1) -> Dapr Service (3) -> Dapr Binding (2) -> +### Dapr PubSub (4) -> completed-state (otomatik) +### ============================================================ + +### Adim 1: Workflow Baslat (sync - tam zincir) +# @name createExtendedInstance +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "extended-tasks-test-{{$timestamp}}", + "tags": ["integration-test", "task-execution", "dapr-only"], + "attributes": { + "testId": "extended-tasks-dapr-path" + } +} + +@instanceId = {{createExtendedInstance.response.body.$.id}} + +### Adim 2: Durum Kontrol - completed-state olmali +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state + +### Adim 3: Instance Data - Dapr task sonuclari +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}} + +### ============================================================ +### Beklenen Sonuclar: +### - initCompleted: true +### - taskResults.daprHttp.completed: true +### - taskResults.daprService.completed: true +### - taskResults.daprBinding.completed: true +### - taskResults.daprPubSub.completed: true +### ============================================================ diff --git a/api-tests/task-execution/postman-extended-tasks.json b/api-tests/task-execution/postman-extended-tasks.json new file mode 100644 index 0000000..c8f6bc5 --- /dev/null +++ b/api-tests/task-execution/postman-extended-tasks.json @@ -0,0 +1,144 @@ +{ + "info": { + "_postman_id": "382174e0-fc37-4d36-ba7b-653b40194f3c", + "name": "vNext Test - Extended Tasks (Dapr only)", + "description": "extended-tasks-test-workflow: sadece Dapr HTTP/Service/Binding/PubSub zinciri.\nKlasor: api-tests/task-execution (task-execution grubu altinda).", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "variable": [ + { + "key": "baseUrl", + "value": "http://localhost:4201", + "type": "string" + }, + { + "key": "apiVersion", + "value": "1", + "type": "string" + }, + { + "key": "domain", + "value": "core", + "type": "string" + }, + { + "key": "workflow", + "value": "extended-tasks-test-workflow", + "type": "string" + } + ], + "item": [ + { + "name": "Dapr zinciri: extended-tasks-test-workflow", + "item": [ + { + "name": "Adim 1: Workflow baslat (sync)", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"extended-tasks-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"task-execution\", \"dapr-only\"],\n \"attributes\": {\n \"testId\": \"extended-tasks-dapr-path\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "if (jsonData && jsonData.id) {", + " pm.collectionVariables.set(\"instanceId\", jsonData.id);", + " console.log(\"instanceId set to: \" + jsonData.id);", + "}" + ] + } + } + ] + }, + { + "name": "Adim 2: Durum (completed-state)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{instanceId}}", + "functions", + "state" + ] + } + }, + "response": [] + }, + { + "name": "Adim 3: Instance (Dapr taskResults)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{instanceId}}" + ] + } + }, + "response": [] + } + ] + } + ] +} diff --git a/api-tests/task-execution/postman-task-execution.json b/api-tests/task-execution/postman-task-execution.json index 17f7cf1..b5b2907 100644 --- a/api-tests/task-execution/postman-task-execution.json +++ b/api-tests/task-execution/postman-task-execution.json @@ -2,7 +2,7 @@ "info": { "_postman_id": "2835031e-c465-4e76-b09c-a4b3b3e63246", "name": "vNext Test - Task Execution", - "description": "Auto-generated from task-execution-test-workflow.http.\nvNext workflow integration test collection for task-execution.", + "description": "Auto-generated from task-execution-test-workflow.http.\nAna zincir: task-execution-test-workflow. Dapr-only senaryo: ayri koleksiyon postman-extended-tasks.json (extended-tasks-test-workflow).", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "variable": [ @@ -101,7 +101,7 @@ ] }, { - "name": "Adim 2: Durum kontrol (completed-state beklenir)", + "name": "Adim 2: Durum kontrol (human-task-state beklenir)", "request": { "method": "GET", "header": [], @@ -126,7 +126,97 @@ "response": [] }, { - "name": "Adim 3: Instance data dogrula", + "name": "Adim 3: Instance data (notification / trigger / subprocess / getInstances)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/data", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{taskExecInstanceId}}", + "functions", + "data" + ] + } + }, + "response": [] + }, + { + "name": "Adim 4: Human task onayla (approve-human-task)", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/transitions/approve-human-task", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{taskExecInstanceId}}", + "transitions", + "approve-human-task" + ] + }, + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [] + }, + { + "name": "Adim 5: Durum kontrol (completed-state)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{taskExecInstanceId}}", + "functions", + "state" + ] + } + }, + "response": [] + }, + { + "name": "Adim 6: Final instance data", "request": { "method": "GET", "header": [], diff --git a/api-tests/task-execution/task-execution-test-workflow.http b/api-tests/task-execution/task-execution-test-workflow.http index ead15b1..da86b16 100644 --- a/api-tests/task-execution/task-execution-test-workflow.http +++ b/api-tests/task-execution/task-execution-test-workflow.http @@ -7,12 +7,13 @@ ############################################################################### # TEST 1: Full task execution chain -# HTTP task -> Script task -> Cross workflow -> Start Flow (type 11) -> -# Get Instance Data (type 13) -> Completed +# HTTP -> Script -> Cross workflow -> Start Flow (11) -> Get Instance Data (13) +# -> Notification (10) -> TriggerTransition (12) -> SubProcess (14) +# -> GetInstances (15) -> Human Task (5) -> PATCH approve -> Completed # Requires: MocklabBaseUrl config, Mocklab seeded (POST /api/test/process) ############################################################################### -### Adim 1: Ana workflow baslat (sync) +### Adim 1: Ana workflow baslat (sync zincir human-task-state'te durur) # @name createTaskExecInstance POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json @@ -25,13 +26,27 @@ Content-Type: application/json @taskExecInstanceId = {{createTaskExecInstance.response.body.$.id}} -### Adim 2: Durum kontrol (completed-state beklenir) +### Adim 2: Durum kontrol (human-task-state beklenir) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/state -### Adim 3: Instance data dogrula -# Beklenen bayraklar: testId, httpTaskCompleted, scriptProcessed, -# crossWorkflowCompleted, startFlowCompleted, startedInstanceId, -# getInstanceDataCompleted, remoteInstanceData +### Adim 3: Instance data (notification / trigger / subprocess / getInstances bayraklari) +# Beklenen: testId, httpTaskCompleted, scriptProcessed, crossWorkflowCompleted, +# startFlowCompleted, startedInstanceId, getInstanceDataCompleted, +# taskResults.notification.completed, taskResults.triggerTransition.completed, +# taskResults.subprocess.completed, taskResults.getInstances.completed, +# taskResults.humanTask.completed +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/data + +### Adim 4: Human task onayla (manuel transition) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/transitions/approve-human-task +Content-Type: application/json + +{} + +### Adim 5: Durum kontrol (completed-state) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/state + +### Adim 6: Final instance data GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/data ############################################################################### diff --git a/core.link.json b/core.link.json new file mode 100644 index 0000000..d0dc643 --- /dev/null +++ b/core.link.json @@ -0,0 +1,5 @@ +{ + "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", + "domain": "core", + "importedAt": "2026-04-30T09:51:04.502Z" +} \ No newline at end of file diff --git a/core/Tasks/extended-tasks/dapr-binding-task.json b/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-binding-task.json similarity index 79% rename from core/Tasks/extended-tasks/dapr-binding-task.json rename to core/Tasks/task-execution/extended-tasks-test-workflow/dapr-binding-task.json index 716c205..ccda07f 100644 --- a/core/Tasks/extended-tasks/dapr-binding-task.json +++ b/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-binding-task.json @@ -4,7 +4,7 @@ "domain": "core", "flow": "sys-tasks", "flowVersion": "1.0.0", - "tags": ["integration-test", "extended-tasks", "dapr-binding"], + "tags": ["integration-test", "task-execution", "dapr-binding"], "attributes": { "type": "2", "config": { diff --git a/core/Tasks/extended-tasks/dapr-http-task.json b/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-http-task.json similarity index 81% rename from core/Tasks/extended-tasks/dapr-http-task.json rename to core/Tasks/task-execution/extended-tasks-test-workflow/dapr-http-task.json index cd48f1f..e4bc8ba 100644 --- a/core/Tasks/extended-tasks/dapr-http-task.json +++ b/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-http-task.json @@ -4,7 +4,7 @@ "domain": "core", "flow": "sys-tasks", "flowVersion": "1.0.0", - "tags": ["integration-test", "extended-tasks", "dapr-http"], + "tags": ["integration-test", "task-execution", "dapr-http"], "attributes": { "type": "1", "config": { diff --git a/core/Tasks/extended-tasks/dapr-pubsub-task.json b/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-pubsub-task.json similarity index 79% rename from core/Tasks/extended-tasks/dapr-pubsub-task.json rename to core/Tasks/task-execution/extended-tasks-test-workflow/dapr-pubsub-task.json index 2d80175..8d7acda 100644 --- a/core/Tasks/extended-tasks/dapr-pubsub-task.json +++ b/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-pubsub-task.json @@ -4,7 +4,7 @@ "domain": "core", "flow": "sys-tasks", "flowVersion": "1.0.0", - "tags": ["integration-test", "extended-tasks", "dapr-pubsub"], + "tags": ["integration-test", "task-execution", "dapr-pubsub"], "attributes": { "type": "4", "config": { diff --git a/core/Tasks/extended-tasks/dapr-service-task.json b/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-service-task.json similarity index 81% rename from core/Tasks/extended-tasks/dapr-service-task.json rename to core/Tasks/task-execution/extended-tasks-test-workflow/dapr-service-task.json index 618f4ad..3ff0db5 100644 --- a/core/Tasks/extended-tasks/dapr-service-task.json +++ b/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-service-task.json @@ -4,7 +4,7 @@ "domain": "core", "flow": "sys-tasks", "flowVersion": "1.0.0", - "tags": ["integration-test", "extended-tasks", "dapr-service"], + "tags": ["integration-test", "task-execution", "dapr-service"], "attributes": { "type": "3", "config": { diff --git a/core/Tasks/task-execution/get-instance-data-task.json b/core/Tasks/task-execution/task-execution-test-workflow/get-instance-data-task.json similarity index 100% rename from core/Tasks/task-execution/get-instance-data-task.json rename to core/Tasks/task-execution/task-execution-test-workflow/get-instance-data-task.json diff --git a/core/Tasks/extended-tasks/get-instances-task.json b/core/Tasks/task-execution/task-execution-test-workflow/get-instances-task.json similarity index 81% rename from core/Tasks/extended-tasks/get-instances-task.json rename to core/Tasks/task-execution/task-execution-test-workflow/get-instances-task.json index 4e03a14..ada465f 100644 --- a/core/Tasks/extended-tasks/get-instances-task.json +++ b/core/Tasks/task-execution/task-execution-test-workflow/get-instances-task.json @@ -4,7 +4,7 @@ "domain": "core", "flow": "sys-tasks", "flowVersion": "1.0.0", - "tags": ["integration-test", "extended-tasks", "get-instances"], + "tags": ["integration-test", "task-execution", "get-instances"], "attributes": { "type": "15", "config": { diff --git a/core/Tasks/task-execution/http-process-task.json b/core/Tasks/task-execution/task-execution-test-workflow/http-process-task.json similarity index 100% rename from core/Tasks/task-execution/http-process-task.json rename to core/Tasks/task-execution/task-execution-test-workflow/http-process-task.json diff --git a/core/Tasks/extended-tasks/human-task.json b/core/Tasks/task-execution/task-execution-test-workflow/human-task.json similarity index 82% rename from core/Tasks/extended-tasks/human-task.json rename to core/Tasks/task-execution/task-execution-test-workflow/human-task.json index ffa89f7..26e6c73 100644 --- a/core/Tasks/extended-tasks/human-task.json +++ b/core/Tasks/task-execution/task-execution-test-workflow/human-task.json @@ -4,7 +4,7 @@ "domain": "core", "flow": "sys-tasks", "flowVersion": "1.0.0", - "tags": ["integration-test", "extended-tasks", "human-task"], + "tags": ["integration-test", "task-execution", "human-task"], "attributes": { "type": "5", "config": { diff --git a/core/Tasks/extended-tasks/notification-task.json b/core/Tasks/task-execution/task-execution-test-workflow/notification-task.json similarity index 80% rename from core/Tasks/extended-tasks/notification-task.json rename to core/Tasks/task-execution/task-execution-test-workflow/notification-task.json index 0a07c01..ba5ff6f 100644 --- a/core/Tasks/extended-tasks/notification-task.json +++ b/core/Tasks/task-execution/task-execution-test-workflow/notification-task.json @@ -4,7 +4,7 @@ "domain": "core", "flow": "sys-tasks", "flowVersion": "1.0.0", - "tags": ["integration-test", "extended-tasks", "notification"], + "tags": ["integration-test", "task-execution", "notification"], "attributes": { "type": "10", "config": { diff --git a/core/Tasks/task-execution/start-flow-task.json b/core/Tasks/task-execution/task-execution-test-workflow/start-flow-task.json similarity index 100% rename from core/Tasks/task-execution/start-flow-task.json rename to core/Tasks/task-execution/task-execution-test-workflow/start-flow-task.json diff --git a/core/Tasks/extended-tasks/subprocess-task.json b/core/Tasks/task-execution/task-execution-test-workflow/subprocess-task.json similarity index 79% rename from core/Tasks/extended-tasks/subprocess-task.json rename to core/Tasks/task-execution/task-execution-test-workflow/subprocess-task.json index b986f08..eebe6a6 100644 --- a/core/Tasks/extended-tasks/subprocess-task.json +++ b/core/Tasks/task-execution/task-execution-test-workflow/subprocess-task.json @@ -4,7 +4,7 @@ "domain": "core", "flow": "sys-tasks", "flowVersion": "1.0.0", - "tags": ["integration-test", "extended-tasks", "subprocess"], + "tags": ["integration-test", "task-execution", "subprocess"], "attributes": { "type": "14", "config": { diff --git a/core/Tasks/task-execution/task-exec-script-task.json b/core/Tasks/task-execution/task-execution-test-workflow/task-exec-script-task.json similarity index 100% rename from core/Tasks/task-execution/task-exec-script-task.json rename to core/Tasks/task-execution/task-execution-test-workflow/task-exec-script-task.json diff --git a/core/Tasks/extended-tasks/trigger-transition-task.json b/core/Tasks/task-execution/task-execution-test-workflow/trigger-transition-task.json similarity index 82% rename from core/Tasks/extended-tasks/trigger-transition-task.json rename to core/Tasks/task-execution/task-execution-test-workflow/trigger-transition-task.json index 7a1b996..4baa617 100644 --- a/core/Tasks/extended-tasks/trigger-transition-task.json +++ b/core/Tasks/task-execution/task-execution-test-workflow/trigger-transition-task.json @@ -4,7 +4,7 @@ "domain": "core", "flow": "sys-tasks", "flowVersion": "1.0.0", - "tags": ["integration-test", "extended-tasks", "trigger-transition"], + "tags": ["integration-test", "task-execution", "trigger-transition"], "attributes": { "type": "12", "config": { diff --git a/core/Workflows/extended-tasks/extended-tasks-test-workflow.json b/core/Workflows/extended-tasks/extended-tasks-test-workflow.json deleted file mode 100644 index 9c6b994..0000000 --- a/core/Workflows/extended-tasks/extended-tasks-test-workflow.json +++ /dev/null @@ -1,644 +0,0 @@ -{ - "key": "extended-tasks-test-workflow", - "version": "1.0.0", - "domain": "core", - "flow": "sys-flows", - "flowVersion": "1.0.0", - "tags": [ - "integration-test", - "extended-tasks", - "dapr-http", - "dapr-binding", - "dapr-service", - "dapr-pubsub", - "human-task", - "notification", - "trigger-transition", - "subprocess", - "get-instances" - ], - "attributes": { - "type": "F", - "timeout": null, - "labels": [ - { - "language": "en-US", - "label": "Extended Tasks Test Workflow" - }, - { - "language": "tr-TR", - "label": "Genisletilmis Gorev Tipleri Test Is Akisi" - } - ], - "functions": [], - "features": [], - "extensions": [], - "sharedTransitions": [], - "startTransition": { - "key": "start-extended-tasks", - "target": "init-state", - "triggerType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Start Extended Tasks Test" - }, - { - "language": "tr-TR", - "label": "Genisletilmis Gorev Testini Baslat" - } - ], - "onExecutionTasks": [] - }, - "states": [ - { - "key": "init-state", - "stateType": 1, - "subType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Initialize" - }, - { - "language": "tr-TR", - "label": "Baslangic" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - { - "order": 1, - "task": { - "key": "script-task", - "version": "1.0.0", - "domain": "core", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/InitExtendedTaskMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEluaXRFeHRlbmRlZFRhc2tNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpCiAgICAgICAgICAgIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKCiAgICAgICAgcmVzdWx0LmluaXRDb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC5pbml0QXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiSW5pdEV4dGVuZGVkVGFza01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" - } - } - ], - "onExits": [], - "transitions": [ - { - "key": "auto-to-dapr-http", - "target": "dapr-http-state", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Auto to Dapr HTTP" - }, - { - "language": "tr-TR", - "label": "Dapr HTTP'ye Otomatik" - } - ], - "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" - }, - "onExecutionTasks": [] - } - ] - }, - { - "key": "dapr-http-state", - "stateType": 2, - "subType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Dapr HTTP Task" - }, - { - "language": "tr-TR", - "label": "Dapr HTTP Gorevi" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - { - "order": 1, - "task": { - "key": "dapr-http-task", - "version": "1.0.0", - "domain": "core", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/DaprHttpMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIERhcHJIdHRwTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpbml0Q29tcGxldGVkIikpIHJlc3VsdC5pbml0Q29tcGxldGVkID0gZGF0YS5pbml0Q29tcGxldGVkOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwclNlcnZpY2UiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJTZXJ2aWNlID0gZGF0YS50YXNrUmVzdWx0cy5kYXByU2VydmljZTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInRyaWdnZXJUcmFuc2l0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJzdWJwcm9jZXNzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzID0gZGF0YS50YXNrUmVzdWx0cy5zdWJwcm9jZXNzOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJIdHRwID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwckh0dHAuY29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwckh0dHAuZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICB2YXIgdGFza1Jlc3BvbnNlID0gY29udGV4dC5Cb2R5OwogICAgICAgIGlmICh0YXNrUmVzcG9uc2UgIT0gbnVsbCAmJiBIYXNQcm9wZXJ0eSh0YXNrUmVzcG9uc2UsICJkYXRhIikpCiAgICAgICAgewogICAgICAgICAgICB2YXIgcmVzcG9uc2VEYXRhID0gdGFza1Jlc3BvbnNlLmRhdGE7CiAgICAgICAgICAgIGlmIChyZXNwb25zZURhdGEgIT0gbnVsbCAmJiBIYXNQcm9wZXJ0eShyZXNwb25zZURhdGEsICJwcm9jZXNzSWQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXBySHR0cC5wcm9jZXNzSWQgPSByZXNwb25zZURhdGEucHJvY2Vzc0lkOwogICAgICAgIH0KCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkRhcHJIdHRwTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" - } - } - ], - "onExits": [], - "transitions": [ - { - "key": "auto-to-dapr-service", - "target": "dapr-service-state", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Auto to Dapr Service" - }, - { - "language": "tr-TR", - "label": "Dapr Service'e Otomatik" - } - ], - "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" - }, - "onExecutionTasks": [] - } - ] - }, - { - "key": "dapr-service-state", - "stateType": 2, - "subType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Dapr Service Task" - }, - { - "language": "tr-TR", - "label": "Dapr Service Gorevi" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - { - "order": 1, - "task": { - "key": "dapr-service-task", - "version": "1.0.0", - "domain": "core", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/DaprServiceMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIERhcHJTZXJ2aWNlTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpbml0Q29tcGxldGVkIikpIHJlc3VsdC5pbml0Q29tcGxldGVkID0gZGF0YS5pbml0Q29tcGxldGVkOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwckh0dHAiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJIdHRwID0gZGF0YS50YXNrUmVzdWx0cy5kYXBySHR0cDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInRyaWdnZXJUcmFuc2l0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJzdWJwcm9jZXNzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzID0gZGF0YS50YXNrUmVzdWx0cy5zdWJwcm9jZXNzOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJTZXJ2aWNlID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwclNlcnZpY2UuY29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwclNlcnZpY2UuZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICB2YXIgdGFza1Jlc3BvbnNlID0gY29udGV4dC5Cb2R5OwogICAgICAgIGlmICh0YXNrUmVzcG9uc2UgIT0gbnVsbCAmJiBIYXNQcm9wZXJ0eSh0YXNrUmVzcG9uc2UsICJkYXRhIikpCiAgICAgICAgewogICAgICAgICAgICB2YXIgcmVzcG9uc2VEYXRhID0gdGFza1Jlc3BvbnNlLmRhdGE7CiAgICAgICAgICAgIGlmIChyZXNwb25zZURhdGEgIT0gbnVsbCAmJiBIYXNQcm9wZXJ0eShyZXNwb25zZURhdGEsICJwcm9jZXNzSWQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByU2VydmljZS5wcm9jZXNzSWQgPSByZXNwb25zZURhdGEucHJvY2Vzc0lkOwogICAgICAgIH0KCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkRhcHJTZXJ2aWNlTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" - } - } - ], - "onExits": [], - "transitions": [ - { - "key": "auto-to-dapr-binding", - "target": "dapr-binding-state", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Auto to Dapr Binding" - }, - { - "language": "tr-TR", - "label": "Dapr Binding'e Otomatik" - } - ], - "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" - }, - "onExecutionTasks": [] - } - ] - }, - { - "key": "dapr-binding-state", - "stateType": 2, - "subType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Dapr Binding Task" - }, - { - "language": "tr-TR", - "label": "Dapr Binding Gorevi" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - { - "order": 1, - "task": { - "key": "dapr-binding-task", - "version": "1.0.0", - "domain": "core", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/DaprBindingMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIERhcHJCaW5kaW5nTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpbml0Q29tcGxldGVkIikpIHJlc3VsdC5pbml0Q29tcGxldGVkID0gZGF0YS5pbml0Q29tcGxldGVkOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwckh0dHAiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJIdHRwID0gZGF0YS50YXNrUmVzdWx0cy5kYXBySHR0cDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXByU2VydmljZSIpKSByZXN1bHQudGFza1Jlc3VsdHMuZGFwclNlcnZpY2UgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJTZXJ2aWNlOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgIm5vdGlmaWNhdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uID0gZGF0YS50YXNrUmVzdWx0cy5ub3RpZmljYXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAidHJpZ2dlclRyYW5zaXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uID0gZGF0YS50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJnZXRJbnN0YW5jZXMiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IGRhdGEudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgfQoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwckJpbmRpbmcgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByQmluZGluZy5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByQmluZGluZy5leGVjdXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCJEYXByQmluZGluZ01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" - } - } - ], - "onExits": [], - "transitions": [ - { - "key": "auto-to-dapr-pubsub", - "target": "dapr-pubsub-state", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Auto to Dapr PubSub" - }, - { - "language": "tr-TR", - "label": "Dapr PubSub'a Otomatik" - } - ], - "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" - }, - "onExecutionTasks": [] - } - ] - }, - { - "key": "dapr-pubsub-state", - "stateType": 2, - "subType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Dapr PubSub Task" - }, - { - "language": "tr-TR", - "label": "Dapr PubSub Gorevi" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - { - "order": 1, - "task": { - "key": "dapr-pubsub-task", - "version": "1.0.0", - "domain": "core", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/DaprPubSubMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIERhcHJQdWJTdWJNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImluaXRDb21wbGV0ZWQiKSkgcmVzdWx0LmluaXRDb21wbGV0ZWQgPSBkYXRhLmluaXRDb21wbGV0ZWQ7CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0YXNrUmVzdWx0cyIpKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXBySHR0cCIpKSByZXN1bHQudGFza1Jlc3VsdHMuZGFwckh0dHAgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJIdHRwOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImRhcHJTZXJ2aWNlIikpIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByU2VydmljZSA9IGRhdGEudGFza1Jlc3VsdHMuZGFwclNlcnZpY2U7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwckJpbmRpbmciKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJCaW5kaW5nID0gZGF0YS50YXNrUmVzdWx0cy5kYXByQmluZGluZzsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInRyaWdnZXJUcmFuc2l0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJzdWJwcm9jZXNzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzID0gZGF0YS50YXNrUmVzdWx0cy5zdWJwcm9jZXNzOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByUHViU3ViLmNvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIuZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiRGFwclB1YlN1Yk1hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" - } - } - ], - "onExits": [], - "transitions": [ - { - "key": "auto-to-notification", - "target": "notification-state", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Auto to Notification" - }, - { - "language": "tr-TR", - "label": "Bildirime Otomatik" - } - ], - "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" - }, - "onExecutionTasks": [] - } - ] - }, - { - "key": "notification-state", - "stateType": 2, - "subType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Notification Task" - }, - { - "language": "tr-TR", - "label": "Bildirim Gorevi" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - { - "order": 1, - "task": { - "key": "notification-task", - "version": "1.0.0", - "domain": "core", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/NotificationMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIE5vdGlmaWNhdGlvbk1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaW5pdENvbXBsZXRlZCIpKSByZXN1bHQuaW5pdENvbXBsZXRlZCA9IGRhdGEuaW5pdENvbXBsZXRlZDsKCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRhc2tSZXN1bHRzIikpCiAgICAgICAgewogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImRhcHJIdHRwIikpIHJlc3VsdC50YXNrUmVzdWx0cy5kYXBySHR0cCA9IGRhdGEudGFza1Jlc3VsdHMuZGFwckh0dHA7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwclNlcnZpY2UiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJTZXJ2aWNlID0gZGF0YS50YXNrUmVzdWx0cy5kYXByU2VydmljZTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXByQmluZGluZyIpKSByZXN1bHQudGFza1Jlc3VsdHMuZGFwckJpbmRpbmcgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJCaW5kaW5nOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImRhcHJQdWJTdWIiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJQdWJTdWI7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAidHJpZ2dlclRyYW5zaXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uID0gZGF0YS50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJnZXRJbnN0YW5jZXMiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IGRhdGEudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgfQoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uLmNvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbi5leGVjdXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCJOb3RpZmljYXRpb25NYXBwaW5nIGNvbXBsZXRlZCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" - } - } - ], - "onExits": [], - "transitions": [ - { - "key": "auto-to-trigger-transition", - "target": "trigger-transition-state", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Auto to Trigger Transition" - }, - { - "language": "tr-TR", - "label": "Trigger Transition'a Otomatik" - } - ], - "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" - }, - "onExecutionTasks": [] - } - ] - }, - { - "key": "trigger-transition-state", - "stateType": 2, - "subType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Trigger Transition Task" - }, - { - "language": "tr-TR", - "label": "Gecis Tetikleme Gorevi" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - { - "order": 1, - "task": { - "key": "trigger-transition-task", - "version": "1.0.0", - "domain": "core", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/TriggerTransitionMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFRyaWdnZXJUcmFuc2l0aW9uTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpbml0Q29tcGxldGVkIikpIHJlc3VsdC5pbml0Q29tcGxldGVkID0gZGF0YS5pbml0Q29tcGxldGVkOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwckh0dHAiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJIdHRwID0gZGF0YS50YXNrUmVzdWx0cy5kYXBySHR0cDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXByU2VydmljZSIpKSByZXN1bHQudGFza1Jlc3VsdHMuZGFwclNlcnZpY2UgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJTZXJ2aWNlOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImRhcHJCaW5kaW5nIikpIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByQmluZGluZyA9IGRhdGEudGFza1Jlc3VsdHMuZGFwckJpbmRpbmc7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwclB1YlN1YiIpKSByZXN1bHQudGFza1Jlc3VsdHMuZGFwclB1YlN1YiA9IGRhdGEudGFza1Jlc3VsdHMuZGFwclB1YlN1YjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImdldEluc3RhbmNlcyIpKSByZXN1bHQudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzID0gZGF0YS50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXM7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAic3VicHJvY2VzcyIpKSByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2VzcyA9IGRhdGEudGFza1Jlc3VsdHMuc3VicHJvY2VzczsKICAgICAgICB9CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uLmNvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uLmV4ZWN1dGVkQXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlRyaWdnZXJUcmFuc2l0aW9uTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" - } - } - ], - "onExits": [], - "transitions": [ - { - "key": "auto-to-subprocess", - "target": "subprocess-state", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Auto to SubProcess" - }, - { - "language": "tr-TR", - "label": "SubProcess'e Otomatik" - } - ], - "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" - }, - "onExecutionTasks": [] - } - ] - }, - { - "key": "subprocess-state", - "stateType": 2, - "subType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "SubProcess Task" - }, - { - "language": "tr-TR", - "label": "Alt Surec Gorevi" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - { - "order": 1, - "task": { - "key": "subprocess-task", - "version": "1.0.0", - "domain": "core", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/SubProcessMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFN1YlByb2Nlc3NNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImluaXRDb21wbGV0ZWQiKSkgcmVzdWx0LmluaXRDb21wbGV0ZWQgPSBkYXRhLmluaXRDb21wbGV0ZWQ7CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0YXNrUmVzdWx0cyIpKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXBySHR0cCIpKSByZXN1bHQudGFza1Jlc3VsdHMuZGFwckh0dHAgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJIdHRwOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImRhcHJTZXJ2aWNlIikpIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByU2VydmljZSA9IGRhdGEudGFza1Jlc3VsdHMuZGFwclNlcnZpY2U7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwckJpbmRpbmciKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJCaW5kaW5nID0gZGF0YS50YXNrUmVzdWx0cy5kYXByQmluZGluZzsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXByUHViU3ViIikpIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByUHViU3ViID0gZGF0YS50YXNrUmVzdWx0cy5kYXByUHViU3ViOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgIm5vdGlmaWNhdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uID0gZGF0YS50YXNrUmVzdWx0cy5ub3RpZmljYXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAidHJpZ2dlclRyYW5zaXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uID0gZGF0YS50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJnZXRJbnN0YW5jZXMiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IGRhdGEudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzLmNvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MuZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU3ViUHJvY2Vzc01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" - } - } - ], - "onExits": [], - "transitions": [ - { - "key": "auto-to-get-instances", - "target": "get-instances-state", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Auto to GetInstances" - }, - { - "language": "tr-TR", - "label": "GetInstances'a Otomatik" - } - ], - "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" - }, - "onExecutionTasks": [] - } - ] - }, - { - "key": "get-instances-state", - "stateType": 2, - "subType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "GetInstances Task" - }, - { - "language": "tr-TR", - "label": "Instance Listesi Gorevi" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - { - "order": 1, - "task": { - "key": "get-instances-task", - "version": "1.0.0", - "domain": "core", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/GetInstancesMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEdldEluc3RhbmNlc01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaW5pdENvbXBsZXRlZCIpKSByZXN1bHQuaW5pdENvbXBsZXRlZCA9IGRhdGEuaW5pdENvbXBsZXRlZDsKCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRhc2tSZXN1bHRzIikpCiAgICAgICAgewogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImRhcHJIdHRwIikpIHJlc3VsdC50YXNrUmVzdWx0cy5kYXBySHR0cCA9IGRhdGEudGFza1Jlc3VsdHMuZGFwckh0dHA7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwclNlcnZpY2UiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJTZXJ2aWNlID0gZGF0YS50YXNrUmVzdWx0cy5kYXByU2VydmljZTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXByQmluZGluZyIpKSByZXN1bHQudGFza1Jlc3VsdHMuZGFwckJpbmRpbmcgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJCaW5kaW5nOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImRhcHJQdWJTdWIiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJQdWJTdWI7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgfQoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzLmNvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcy5leGVjdXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIHZhciB0YXNrUmVzcG9uc2UgPSBjb250ZXh0LkJvZHk7CiAgICAgICAgaWYgKHRhc2tSZXNwb25zZSAhPSBudWxsICYmIEhhc1Byb3BlcnR5KHRhc2tSZXNwb25zZSwgImRhdGEiKSkKICAgICAgICB7CiAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMucmVzcG9uc2VSZWNlaXZlZCA9IHRydWU7CiAgICAgICAgfQoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiR2V0SW5zdGFuY2VzTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" - } - } - ], - "onExits": [], - "transitions": [ - { - "key": "auto-to-human-task", - "target": "human-task-state", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Auto to Human Task" - }, - { - "language": "tr-TR", - "label": "Insan Gorevine Otomatik" - } - ], - "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" - }, - "onExecutionTasks": [] - } - ] - }, - { - "key": "human-task-state", - "stateType": 2, - "subType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Human Task (Approval)" - }, - { - "language": "tr-TR", - "label": "Insan Gorevi (Onay)" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - { - "order": 1, - "task": { - "key": "human-task", - "version": "1.0.0", - "domain": "core", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/HumanTaskMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEh1bWFuVGFza01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaW5pdENvbXBsZXRlZCIpKSByZXN1bHQuaW5pdENvbXBsZXRlZCA9IGRhdGEuaW5pdENvbXBsZXRlZDsKCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRhc2tSZXN1bHRzIikpCiAgICAgICAgewogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImRhcHJIdHRwIikpIHJlc3VsdC50YXNrUmVzdWx0cy5kYXBySHR0cCA9IGRhdGEudGFza1Jlc3VsdHMuZGFwckh0dHA7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwclNlcnZpY2UiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJTZXJ2aWNlID0gZGF0YS50YXNrUmVzdWx0cy5kYXByU2VydmljZTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXByQmluZGluZyIpKSByZXN1bHQudGFza1Jlc3VsdHMuZGFwckJpbmRpbmcgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJCaW5kaW5nOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImRhcHJQdWJTdWIiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJQdWJTdWI7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImdldEluc3RhbmNlcyIpKSByZXN1bHQudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzID0gZGF0YS50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXM7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAic3VicHJvY2VzcyIpKSByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2VzcyA9IGRhdGEudGFza1Jlc3VsdHMuc3VicHJvY2VzczsKICAgICAgICB9CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5odW1hblRhc2sgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5odW1hblRhc2suY29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuaHVtYW5UYXNrLmFwcHJvdmVkQXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkh1bWFuVGFza01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" - } - } - ], - "onExits": [], - "transitions": [ - { - "key": "approve-human-task", - "target": "completed-state", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Approve Human Task" - }, - { - "language": "tr-TR", - "label": "Insan Gorevini Onayla" - } - ], - "onExecutionTasks": [] - } - ] - }, - { - "key": "completed-state", - "stateType": 3, - "subType": 1, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Completed" - }, - { - "language": "tr-TR", - "label": "Tamamlandi" - } - ], - "view": null, - "subFlow": null, - "onEntries": [], - "onExits": [], - "transitions": [] - } - ] - } -} \ No newline at end of file diff --git a/core/Workflows/extended-tasks/src/GetInstancesMapping.csx b/core/Workflows/extended-tasks/src/GetInstancesMapping.csx deleted file mode 100644 index 0baead0..0000000 --- a/core/Workflows/extended-tasks/src/GetInstancesMapping.csx +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Dynamic; -using System.Threading.Tasks; -using BBT.Workflow.Scripting; -using BBT.Workflow.Definitions; - -public class GetInstancesMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public Task OutputHandler(ScriptContext context) - { - var data = context.Instance.Data; - dynamic result = new ExpandoObject(); - - if (HasProperty(data, "testId")) result.testId = data.testId; - if (HasProperty(data, "initCompleted")) result.initCompleted = data.initCompleted; - - result.taskResults = new ExpandoObject(); - if (HasProperty(data, "taskResults")) - { - if (HasProperty(data.taskResults, "daprHttp")) result.taskResults.daprHttp = data.taskResults.daprHttp; - if (HasProperty(data.taskResults, "daprService")) result.taskResults.daprService = data.taskResults.daprService; - if (HasProperty(data.taskResults, "daprBinding")) result.taskResults.daprBinding = data.taskResults.daprBinding; - if (HasProperty(data.taskResults, "daprPubSub")) result.taskResults.daprPubSub = data.taskResults.daprPubSub; - if (HasProperty(data.taskResults, "notification")) result.taskResults.notification = data.taskResults.notification; - if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; - if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; - } - - result.taskResults.getInstances = new ExpandoObject(); - result.taskResults.getInstances.completed = true; - result.taskResults.getInstances.executedAt = DateTime.UtcNow.ToString("o"); - - var taskResponse = context.Body; - if (taskResponse != null && HasProperty(taskResponse, "data")) - { - result.taskResults.getInstances.responseReceived = true; - } - - LogInformation("GetInstancesMapping completed"); - return Task.FromResult(new ScriptResponse { Data = result }); - } -} diff --git a/core/Workflows/extended-tasks/src/HumanTaskMapping.csx b/core/Workflows/extended-tasks/src/HumanTaskMapping.csx deleted file mode 100644 index 9f8a94e..0000000 --- a/core/Workflows/extended-tasks/src/HumanTaskMapping.csx +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Dynamic; -using System.Threading.Tasks; -using BBT.Workflow.Scripting; -using BBT.Workflow.Definitions; - -public class HumanTaskMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public Task OutputHandler(ScriptContext context) - { - var data = context.Instance.Data; - dynamic result = new ExpandoObject(); - - if (HasProperty(data, "testId")) result.testId = data.testId; - if (HasProperty(data, "initCompleted")) result.initCompleted = data.initCompleted; - - result.taskResults = new ExpandoObject(); - if (HasProperty(data, "taskResults")) - { - if (HasProperty(data.taskResults, "daprHttp")) result.taskResults.daprHttp = data.taskResults.daprHttp; - if (HasProperty(data.taskResults, "daprService")) result.taskResults.daprService = data.taskResults.daprService; - if (HasProperty(data.taskResults, "daprBinding")) result.taskResults.daprBinding = data.taskResults.daprBinding; - if (HasProperty(data.taskResults, "daprPubSub")) result.taskResults.daprPubSub = data.taskResults.daprPubSub; - if (HasProperty(data.taskResults, "notification")) result.taskResults.notification = data.taskResults.notification; - if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; - if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; - if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; - } - - result.taskResults.humanTask = new ExpandoObject(); - result.taskResults.humanTask.completed = true; - result.taskResults.humanTask.approvedAt = DateTime.UtcNow.ToString("o"); - - LogInformation("HumanTaskMapping completed"); - return Task.FromResult(new ScriptResponse { Data = result }); - } -} diff --git a/core/Workflows/extended-tasks/src/NotificationMapping.csx b/core/Workflows/extended-tasks/src/NotificationMapping.csx deleted file mode 100644 index 2e6a6d7..0000000 --- a/core/Workflows/extended-tasks/src/NotificationMapping.csx +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Dynamic; -using System.Threading.Tasks; -using BBT.Workflow.Scripting; -using BBT.Workflow.Definitions; - -public class NotificationMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public Task OutputHandler(ScriptContext context) - { - var data = context.Instance.Data; - dynamic result = new ExpandoObject(); - - if (HasProperty(data, "testId")) result.testId = data.testId; - if (HasProperty(data, "initCompleted")) result.initCompleted = data.initCompleted; - - result.taskResults = new ExpandoObject(); - if (HasProperty(data, "taskResults")) - { - if (HasProperty(data.taskResults, "daprHttp")) result.taskResults.daprHttp = data.taskResults.daprHttp; - if (HasProperty(data.taskResults, "daprService")) result.taskResults.daprService = data.taskResults.daprService; - if (HasProperty(data.taskResults, "daprBinding")) result.taskResults.daprBinding = data.taskResults.daprBinding; - if (HasProperty(data.taskResults, "daprPubSub")) result.taskResults.daprPubSub = data.taskResults.daprPubSub; - if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; - if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; - if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; - } - - result.taskResults.notification = new ExpandoObject(); - result.taskResults.notification.completed = true; - result.taskResults.notification.executedAt = DateTime.UtcNow.ToString("o"); - - LogInformation("NotificationMapping completed"); - return Task.FromResult(new ScriptResponse { Data = result }); - } -} diff --git a/core/Workflows/extended-tasks/src/SubProcessMapping.csx b/core/Workflows/extended-tasks/src/SubProcessMapping.csx deleted file mode 100644 index 66821ef..0000000 --- a/core/Workflows/extended-tasks/src/SubProcessMapping.csx +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Dynamic; -using System.Threading.Tasks; -using BBT.Workflow.Scripting; -using BBT.Workflow.Definitions; - -public class SubProcessMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public Task OutputHandler(ScriptContext context) - { - var data = context.Instance.Data; - dynamic result = new ExpandoObject(); - - if (HasProperty(data, "testId")) result.testId = data.testId; - if (HasProperty(data, "initCompleted")) result.initCompleted = data.initCompleted; - - result.taskResults = new ExpandoObject(); - if (HasProperty(data, "taskResults")) - { - if (HasProperty(data.taskResults, "daprHttp")) result.taskResults.daprHttp = data.taskResults.daprHttp; - if (HasProperty(data.taskResults, "daprService")) result.taskResults.daprService = data.taskResults.daprService; - if (HasProperty(data.taskResults, "daprBinding")) result.taskResults.daprBinding = data.taskResults.daprBinding; - if (HasProperty(data.taskResults, "daprPubSub")) result.taskResults.daprPubSub = data.taskResults.daprPubSub; - if (HasProperty(data.taskResults, "notification")) result.taskResults.notification = data.taskResults.notification; - if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; - if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; - } - - result.taskResults.subprocess = new ExpandoObject(); - result.taskResults.subprocess.completed = true; - result.taskResults.subprocess.executedAt = DateTime.UtcNow.ToString("o"); - - LogInformation("SubProcessMapping completed"); - return Task.FromResult(new ScriptResponse { Data = result }); - } -} diff --git a/core/Workflows/extended-tasks/src/TriggerTransitionMapping.csx b/core/Workflows/extended-tasks/src/TriggerTransitionMapping.csx deleted file mode 100644 index 01da003..0000000 --- a/core/Workflows/extended-tasks/src/TriggerTransitionMapping.csx +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Dynamic; -using System.Threading.Tasks; -using BBT.Workflow.Scripting; -using BBT.Workflow.Definitions; - -public class TriggerTransitionMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public Task OutputHandler(ScriptContext context) - { - var data = context.Instance.Data; - dynamic result = new ExpandoObject(); - - if (HasProperty(data, "testId")) result.testId = data.testId; - if (HasProperty(data, "initCompleted")) result.initCompleted = data.initCompleted; - - result.taskResults = new ExpandoObject(); - if (HasProperty(data, "taskResults")) - { - if (HasProperty(data.taskResults, "daprHttp")) result.taskResults.daprHttp = data.taskResults.daprHttp; - if (HasProperty(data.taskResults, "daprService")) result.taskResults.daprService = data.taskResults.daprService; - if (HasProperty(data.taskResults, "daprBinding")) result.taskResults.daprBinding = data.taskResults.daprBinding; - if (HasProperty(data.taskResults, "daprPubSub")) result.taskResults.daprPubSub = data.taskResults.daprPubSub; - if (HasProperty(data.taskResults, "notification")) result.taskResults.notification = data.taskResults.notification; - if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; - if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; - } - - result.taskResults.triggerTransition = new ExpandoObject(); - result.taskResults.triggerTransition.completed = true; - result.taskResults.triggerTransition.executedAt = DateTime.UtcNow.ToString("o"); - - LogInformation("TriggerTransitionMapping completed"); - return Task.FromResult(new ScriptResponse { Data = result }); - } -} diff --git a/core/Workflows/lifecycle-transitions/.meta/lifecycle-transitions-test-workflow.diagram.json b/core/Workflows/lifecycle-transitions/.meta/lifecycle-transitions-test-workflow.diagram.json index 37f1e34..a89848c 100644 --- a/core/Workflows/lifecycle-transitions/.meta/lifecycle-transitions-test-workflow.diagram.json +++ b/core/Workflows/lifecycle-transitions/.meta/lifecycle-transitions-test-workflow.diagram.json @@ -1,28 +1,28 @@ { "nodePos": { "initialize-state": { - "x": 480, - "y": 380 + "x": 560, + "y": 680 }, "processing-state": { - "x": 840, - "y": 40 + "x": 740, + "y": -40 }, "auto-passed-state": { - "x": 1500, - "y": -140 + "x": 1480, + "y": -310 }, "timer-triggered-state": { - "x": 1640, - "y": 540 + "x": 1730, + "y": 340 }, "pre-complete-state": { - "x": 2140, - "y": 240 + "x": 2240, + "y": 100 }, "default-fallback-state": { - "x": 2180, - "y": -40 + "x": 2720, + "y": -100 }, "auto-failed-state": { "x": 1120, diff --git a/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json b/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json index 3aa97de..d7bfa73 100644 --- a/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json +++ b/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json @@ -1,32 +1,52 @@ { "nodePos": { "http-task-state": { - "x": 340, - "y": -180 + "x": 690, + "y": 320 }, "script-processing-state": { - "x": 500, - "y": 560 + "x": 950, + "y": 20 }, "cross-workflow-state": { - "x": 980, - "y": 260 + "x": 1220, + "y": 320 }, "start-flow-state": { - "x": 1200, - "y": -220 + "x": 1520, + "y": 40 }, "get-instance-data-state": { - "x": 1880, - "y": 280 + "x": 1750, + "y": 350 }, "completed-state": { - "x": 2480, - "y": 60 + "x": 3110, + "y": 360 }, "__start__": { - "x": -296, - "y": 100 + "x": 540, + "y": 60 + }, + "human-task-state": { + "x": 2840, + "y": -10 + }, + "get-instances-state": { + "x": 2600, + "y": 380 + }, + "subprocess-state": { + "x": 2460, + "y": -10 + }, + "trigger-transition-state": { + "x": 2180, + "y": 380 + }, + "notification-state": { + "x": 2000, + "y": 20 } } } \ No newline at end of file diff --git a/core/Workflows/task-execution/extended-tasks-test-workflow.json b/core/Workflows/task-execution/extended-tasks-test-workflow.json new file mode 100644 index 0000000..99f20d8 --- /dev/null +++ b/core/Workflows/task-execution/extended-tasks-test-workflow.json @@ -0,0 +1,358 @@ +{ + "key": "extended-tasks-test-workflow", + "version": "1.0.0", + "domain": "core", + "flow": "sys-flows", + "flowVersion": "1.0.0", + "tags": [ + "integration-test", + "task-execution", + "dapr-http", + "dapr-binding", + "dapr-service", + "dapr-pubsub" + ], + "attributes": { + "type": "F", + "timeout": null, + "labels": [ + { + "language": "en-US", + "label": "Extended Tasks (Dapr) Test Workflow" + }, + { + "language": "tr-TR", + "label": "Genisletilmis Gorevler (Dapr) Test Is Akisi" + } + ], + "functions": [], + "features": [], + "extensions": [], + "sharedTransitions": [], + "startTransition": { + "key": "start-extended-tasks", + "target": "init-state", + "triggerType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Start Extended Tasks Test" + }, + { + "language": "tr-TR", + "label": "Genisletilmis Gorev Testini Baslat" + } + ], + "onExecutionTasks": [] + }, + "states": [ + { + "key": "init-state", + "stateType": 1, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Initialize" + }, + { + "language": "tr-TR", + "label": "Baslangic" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "script-task", + "version": "1.0.0", + "domain": "core", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/extended-tasks-test-workflow/InitExtendedTaskMapping.csx", + "code": "" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "auto-to-dapr-http", + "target": "dapr-http-state", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to Dapr HTTP" + }, + { + "language": "tr-TR", + "label": "Dapr HTTP'ye Otomatik" + } + ], + "rule": { + "location": "./src/shared/AlwaysTrueRule.csx", + "code": "" + }, + "onExecutionTasks": [] + } + ] + }, + { + "key": "dapr-http-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Dapr HTTP Task" + }, + { + "language": "tr-TR", + "label": "Dapr HTTP Gorevi" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "dapr-http-task", + "version": "1.0.0", + "domain": "core", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/extended-tasks-test-workflow/DaprHttpMapping.csx", + "code": "" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "auto-to-dapr-service", + "target": "dapr-service-state", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to Dapr Service" + }, + { + "language": "tr-TR", + "label": "Dapr Service'e Otomatik" + } + ], + "rule": { + "location": "./src/shared/AlwaysTrueRule.csx", + "code": "" + }, + "onExecutionTasks": [] + } + ] + }, + { + "key": "dapr-service-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Dapr Service Task" + }, + { + "language": "tr-TR", + "label": "Dapr Service Gorevi" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "dapr-service-task", + "version": "1.0.0", + "domain": "core", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/extended-tasks-test-workflow/DaprServiceMapping.csx", + "code": "" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "auto-to-dapr-binding", + "target": "dapr-binding-state", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to Dapr Binding" + }, + { + "language": "tr-TR", + "label": "Dapr Binding'e Otomatik" + } + ], + "rule": { + "location": "./src/shared/AlwaysTrueRule.csx", + "code": "" + }, + "onExecutionTasks": [] + } + ] + }, + { + "key": "dapr-binding-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Dapr Binding Task" + }, + { + "language": "tr-TR", + "label": "Dapr Binding Gorevi" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "dapr-binding-task", + "version": "1.0.0", + "domain": "core", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/extended-tasks-test-workflow/DaprBindingMapping.csx", + "code": "" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "auto-to-dapr-pubsub", + "target": "dapr-pubsub-state", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to Dapr PubSub" + }, + { + "language": "tr-TR", + "label": "Dapr PubSub'a Otomatik" + } + ], + "rule": { + "location": "./src/shared/AlwaysTrueRule.csx", + "code": "" + }, + "onExecutionTasks": [] + } + ] + }, + { + "key": "dapr-pubsub-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Dapr PubSub Task" + }, + { + "language": "tr-TR", + "label": "Dapr PubSub Gorevi" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "dapr-pubsub-task", + "version": "1.0.0", + "domain": "core", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/extended-tasks-test-workflow/DaprPubSubMapping.csx", + "code": "" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "auto-to-completed", + "target": "completed-state", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to Completed" + }, + { + "language": "tr-TR", + "label": "Tamamlanmaya Otomatik" + } + ], + "rule": { + "location": "./src/shared/AlwaysTrueRule.csx", + "code": "" + }, + "onExecutionTasks": [] + } + ] + }, + { + "key": "completed-state", + "stateType": 3, + "subType": 1, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Completed" + }, + { + "language": "tr-TR", + "label": "Tamamlandi" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [] + } + ] + } +} diff --git a/core/Workflows/task-execution/src/AlwaysTrueRule.csx b/core/Workflows/task-execution/src/AlwaysTrueRule.csx deleted file mode 100644 index 3d08a0f..0000000 --- a/core/Workflows/task-execution/src/AlwaysTrueRule.csx +++ /dev/null @@ -1,11 +0,0 @@ -using System.Threading.Tasks; -using BBT.Workflow.Scripting; - -public class AlwaysTrueRule : ScriptBase, IConditionMapping -{ - public Task Handler(ScriptContext context) - { - LogInformation("AlwaysTrueRule: returning true"); - return Task.FromResult(true); - } -} diff --git a/core/Workflows/extended-tasks/src/DaprBindingMapping.csx b/core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprBindingMapping.csx similarity index 100% rename from core/Workflows/extended-tasks/src/DaprBindingMapping.csx rename to core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprBindingMapping.csx diff --git a/core/Workflows/extended-tasks/src/DaprHttpMapping.csx b/core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprHttpMapping.csx similarity index 100% rename from core/Workflows/extended-tasks/src/DaprHttpMapping.csx rename to core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprHttpMapping.csx diff --git a/core/Workflows/extended-tasks/src/DaprPubSubMapping.csx b/core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprPubSubMapping.csx similarity index 100% rename from core/Workflows/extended-tasks/src/DaprPubSubMapping.csx rename to core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprPubSubMapping.csx diff --git a/core/Workflows/extended-tasks/src/DaprServiceMapping.csx b/core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprServiceMapping.csx similarity index 100% rename from core/Workflows/extended-tasks/src/DaprServiceMapping.csx rename to core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprServiceMapping.csx diff --git a/core/Workflows/extended-tasks/src/InitExtendedTaskMapping.csx b/core/Workflows/task-execution/src/extended-tasks-test-workflow/InitExtendedTaskMapping.csx similarity index 100% rename from core/Workflows/extended-tasks/src/InitExtendedTaskMapping.csx rename to core/Workflows/task-execution/src/extended-tasks-test-workflow/InitExtendedTaskMapping.csx diff --git a/core/Workflows/extended-tasks/src/AlwaysTrueRule.csx b/core/Workflows/task-execution/src/shared/AlwaysTrueRule.csx similarity index 100% rename from core/Workflows/extended-tasks/src/AlwaysTrueRule.csx rename to core/Workflows/task-execution/src/shared/AlwaysTrueRule.csx diff --git a/core/Workflows/task-execution/src/CrossWorkflowMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/CrossWorkflowMapping.csx similarity index 100% rename from core/Workflows/task-execution/src/CrossWorkflowMapping.csx rename to core/Workflows/task-execution/src/task-execution-test-workflow/CrossWorkflowMapping.csx diff --git a/core/Workflows/task-execution/src/GetInstanceDataMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstanceDataMapping.csx similarity index 100% rename from core/Workflows/task-execution/src/GetInstanceDataMapping.csx rename to core/Workflows/task-execution/src/task-execution-test-workflow/GetInstanceDataMapping.csx diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstancesMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstancesMapping.csx new file mode 100644 index 0000000..f6c2ce5 --- /dev/null +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstancesMapping.csx @@ -0,0 +1,59 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; +using BBT.Workflow.Definitions; + +public class GetInstancesMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public Task OutputHandler(ScriptContext context) + { + var data = context.Instance.Data; + dynamic result = new ExpandoObject(); + + if (HasProperty(data, "testId")) result.testId = data.testId; + if (HasProperty(data, "startedAt")) result.startedAt = data.startedAt; + if (HasProperty(data, "httpTaskCompleted")) result.httpTaskCompleted = data.httpTaskCompleted; + if (HasProperty(data, "httpStatusCode")) result.httpStatusCode = data.httpStatusCode; + if (HasProperty(data, "httpIsSuccess")) result.httpIsSuccess = data.httpIsSuccess; + if (HasProperty(data, "processId")) result.processId = data.processId; + if (HasProperty(data, "scriptProcessed")) result.scriptProcessed = data.scriptProcessed; + if (HasProperty(data, "scriptProcessedAt")) result.scriptProcessedAt = data.scriptProcessedAt; + if (HasProperty(data, "crossWorkflowCompleted")) result.crossWorkflowCompleted = data.crossWorkflowCompleted; + if (HasProperty(data, "crossWorkflowAt")) result.crossWorkflowAt = data.crossWorkflowAt; + if (HasProperty(data, "startFlowCompleted")) result.startFlowCompleted = data.startFlowCompleted; + if (HasProperty(data, "startFlowIsSuccess")) result.startFlowIsSuccess = data.startFlowIsSuccess; + if (HasProperty(data, "startedInstanceId")) result.startedInstanceId = data.startedInstanceId; + if (HasProperty(data, "getInstanceDataCompleted")) result.getInstanceDataCompleted = data.getInstanceDataCompleted; + if (HasProperty(data, "getInstanceDataIsSuccess")) result.getInstanceDataIsSuccess = data.getInstanceDataIsSuccess; + if (HasProperty(data, "remoteInstanceData")) result.remoteInstanceData = data.remoteInstanceData; + + result.taskResults = new ExpandoObject(); + if (HasProperty(data, "taskResults")) + { + if (HasProperty(data.taskResults, "notification")) result.taskResults.notification = data.taskResults.notification; + if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; + if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; + if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; + if (HasProperty(data.taskResults, "humanTask")) result.taskResults.humanTask = data.taskResults.humanTask; + } + + result.taskResults.getInstances = new ExpandoObject(); + result.taskResults.getInstances.completed = true; + result.taskResults.getInstances.executedAt = DateTime.UtcNow.ToString("o"); + + var taskResponse = context.Body; + if (taskResponse != null && HasProperty(taskResponse, "data")) + { + result.taskResults.getInstances.responseReceived = true; + } + + LogInformation("GetInstancesMapping completed"); + return Task.FromResult(new ScriptResponse { Data = result }); + } +} diff --git a/core/Workflows/task-execution/src/HttpProcessMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/HttpProcessMapping.csx similarity index 100% rename from core/Workflows/task-execution/src/HttpProcessMapping.csx rename to core/Workflows/task-execution/src/task-execution-test-workflow/HttpProcessMapping.csx diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/HumanTaskMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/HumanTaskMapping.csx new file mode 100644 index 0000000..42728fd --- /dev/null +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/HumanTaskMapping.csx @@ -0,0 +1,53 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; +using BBT.Workflow.Definitions; + +public class HumanTaskMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public Task OutputHandler(ScriptContext context) + { + var data = context.Instance.Data; + dynamic result = new ExpandoObject(); + + if (HasProperty(data, "testId")) result.testId = data.testId; + if (HasProperty(data, "startedAt")) result.startedAt = data.startedAt; + if (HasProperty(data, "httpTaskCompleted")) result.httpTaskCompleted = data.httpTaskCompleted; + if (HasProperty(data, "httpStatusCode")) result.httpStatusCode = data.httpStatusCode; + if (HasProperty(data, "httpIsSuccess")) result.httpIsSuccess = data.httpIsSuccess; + if (HasProperty(data, "processId")) result.processId = data.processId; + if (HasProperty(data, "scriptProcessed")) result.scriptProcessed = data.scriptProcessed; + if (HasProperty(data, "scriptProcessedAt")) result.scriptProcessedAt = data.scriptProcessedAt; + if (HasProperty(data, "crossWorkflowCompleted")) result.crossWorkflowCompleted = data.crossWorkflowCompleted; + if (HasProperty(data, "crossWorkflowAt")) result.crossWorkflowAt = data.crossWorkflowAt; + if (HasProperty(data, "startFlowCompleted")) result.startFlowCompleted = data.startFlowCompleted; + if (HasProperty(data, "startFlowIsSuccess")) result.startFlowIsSuccess = data.startFlowIsSuccess; + if (HasProperty(data, "startedInstanceId")) result.startedInstanceId = data.startedInstanceId; + if (HasProperty(data, "getInstanceDataCompleted")) result.getInstanceDataCompleted = data.getInstanceDataCompleted; + if (HasProperty(data, "getInstanceDataIsSuccess")) result.getInstanceDataIsSuccess = data.getInstanceDataIsSuccess; + if (HasProperty(data, "remoteInstanceData")) result.remoteInstanceData = data.remoteInstanceData; + + result.taskResults = new ExpandoObject(); + if (HasProperty(data, "taskResults")) + { + if (HasProperty(data.taskResults, "notification")) result.taskResults.notification = data.taskResults.notification; + if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; + if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; + if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; + if (HasProperty(data.taskResults, "humanTask")) result.taskResults.humanTask = data.taskResults.humanTask; + } + + result.taskResults.humanTask = new ExpandoObject(); + result.taskResults.humanTask.completed = true; + result.taskResults.humanTask.approvedAt = DateTime.UtcNow.ToString("o"); + + LogInformation("HumanTaskMapping completed"); + return Task.FromResult(new ScriptResponse { Data = result }); + } +} diff --git a/core/Workflows/task-execution/src/InitTaskTestMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/InitTaskTestMapping.csx similarity index 100% rename from core/Workflows/task-execution/src/InitTaskTestMapping.csx rename to core/Workflows/task-execution/src/task-execution-test-workflow/InitTaskTestMapping.csx diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/NotificationMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/NotificationMapping.csx new file mode 100644 index 0000000..05e0def --- /dev/null +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/NotificationMapping.csx @@ -0,0 +1,53 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; +using BBT.Workflow.Definitions; + +public class NotificationMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public Task OutputHandler(ScriptContext context) + { + var data = context.Instance.Data; + dynamic result = new ExpandoObject(); + + if (HasProperty(data, "testId")) result.testId = data.testId; + if (HasProperty(data, "startedAt")) result.startedAt = data.startedAt; + if (HasProperty(data, "httpTaskCompleted")) result.httpTaskCompleted = data.httpTaskCompleted; + if (HasProperty(data, "httpStatusCode")) result.httpStatusCode = data.httpStatusCode; + if (HasProperty(data, "httpIsSuccess")) result.httpIsSuccess = data.httpIsSuccess; + if (HasProperty(data, "processId")) result.processId = data.processId; + if (HasProperty(data, "scriptProcessed")) result.scriptProcessed = data.scriptProcessed; + if (HasProperty(data, "scriptProcessedAt")) result.scriptProcessedAt = data.scriptProcessedAt; + if (HasProperty(data, "crossWorkflowCompleted")) result.crossWorkflowCompleted = data.crossWorkflowCompleted; + if (HasProperty(data, "crossWorkflowAt")) result.crossWorkflowAt = data.crossWorkflowAt; + if (HasProperty(data, "startFlowCompleted")) result.startFlowCompleted = data.startFlowCompleted; + if (HasProperty(data, "startFlowIsSuccess")) result.startFlowIsSuccess = data.startFlowIsSuccess; + if (HasProperty(data, "startedInstanceId")) result.startedInstanceId = data.startedInstanceId; + if (HasProperty(data, "getInstanceDataCompleted")) result.getInstanceDataCompleted = data.getInstanceDataCompleted; + if (HasProperty(data, "getInstanceDataIsSuccess")) result.getInstanceDataIsSuccess = data.getInstanceDataIsSuccess; + if (HasProperty(data, "remoteInstanceData")) result.remoteInstanceData = data.remoteInstanceData; + + result.taskResults = new ExpandoObject(); + if (HasProperty(data, "taskResults")) + { + if (HasProperty(data.taskResults, "notification")) result.taskResults.notification = data.taskResults.notification; + if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; + if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; + if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; + if (HasProperty(data.taskResults, "humanTask")) result.taskResults.humanTask = data.taskResults.humanTask; + } + + result.taskResults.notification = new ExpandoObject(); + result.taskResults.notification.completed = true; + result.taskResults.notification.executedAt = DateTime.UtcNow.ToString("o"); + + LogInformation("NotificationMapping completed"); + return Task.FromResult(new ScriptResponse { Data = result }); + } +} diff --git a/core/Workflows/task-execution/src/ScriptProcessMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/ScriptProcessMapping.csx similarity index 100% rename from core/Workflows/task-execution/src/ScriptProcessMapping.csx rename to core/Workflows/task-execution/src/task-execution-test-workflow/ScriptProcessMapping.csx diff --git a/core/Workflows/task-execution/src/StartFlowMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/StartFlowMapping.csx similarity index 100% rename from core/Workflows/task-execution/src/StartFlowMapping.csx rename to core/Workflows/task-execution/src/task-execution-test-workflow/StartFlowMapping.csx diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx new file mode 100644 index 0000000..a1b6d3f --- /dev/null +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx @@ -0,0 +1,53 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; +using BBT.Workflow.Definitions; + +public class SubProcessMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public Task OutputHandler(ScriptContext context) + { + var data = context.Instance.Data; + dynamic result = new ExpandoObject(); + + if (HasProperty(data, "testId")) result.testId = data.testId; + if (HasProperty(data, "startedAt")) result.startedAt = data.startedAt; + if (HasProperty(data, "httpTaskCompleted")) result.httpTaskCompleted = data.httpTaskCompleted; + if (HasProperty(data, "httpStatusCode")) result.httpStatusCode = data.httpStatusCode; + if (HasProperty(data, "httpIsSuccess")) result.httpIsSuccess = data.httpIsSuccess; + if (HasProperty(data, "processId")) result.processId = data.processId; + if (HasProperty(data, "scriptProcessed")) result.scriptProcessed = data.scriptProcessed; + if (HasProperty(data, "scriptProcessedAt")) result.scriptProcessedAt = data.scriptProcessedAt; + if (HasProperty(data, "crossWorkflowCompleted")) result.crossWorkflowCompleted = data.crossWorkflowCompleted; + if (HasProperty(data, "crossWorkflowAt")) result.crossWorkflowAt = data.crossWorkflowAt; + if (HasProperty(data, "startFlowCompleted")) result.startFlowCompleted = data.startFlowCompleted; + if (HasProperty(data, "startFlowIsSuccess")) result.startFlowIsSuccess = data.startFlowIsSuccess; + if (HasProperty(data, "startedInstanceId")) result.startedInstanceId = data.startedInstanceId; + if (HasProperty(data, "getInstanceDataCompleted")) result.getInstanceDataCompleted = data.getInstanceDataCompleted; + if (HasProperty(data, "getInstanceDataIsSuccess")) result.getInstanceDataIsSuccess = data.getInstanceDataIsSuccess; + if (HasProperty(data, "remoteInstanceData")) result.remoteInstanceData = data.remoteInstanceData; + + result.taskResults = new ExpandoObject(); + if (HasProperty(data, "taskResults")) + { + if (HasProperty(data.taskResults, "notification")) result.taskResults.notification = data.taskResults.notification; + if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; + if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; + if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; + if (HasProperty(data.taskResults, "humanTask")) result.taskResults.humanTask = data.taskResults.humanTask; + } + + result.taskResults.subprocess = new ExpandoObject(); + result.taskResults.subprocess.completed = true; + result.taskResults.subprocess.executedAt = DateTime.UtcNow.ToString("o"); + + LogInformation("SubProcessMapping completed"); + return Task.FromResult(new ScriptResponse { Data = result }); + } +} diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx new file mode 100644 index 0000000..a4fd0a0 --- /dev/null +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx @@ -0,0 +1,53 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; +using BBT.Workflow.Definitions; + +public class TriggerTransitionMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public Task OutputHandler(ScriptContext context) + { + var data = context.Instance.Data; + dynamic result = new ExpandoObject(); + + if (HasProperty(data, "testId")) result.testId = data.testId; + if (HasProperty(data, "startedAt")) result.startedAt = data.startedAt; + if (HasProperty(data, "httpTaskCompleted")) result.httpTaskCompleted = data.httpTaskCompleted; + if (HasProperty(data, "httpStatusCode")) result.httpStatusCode = data.httpStatusCode; + if (HasProperty(data, "httpIsSuccess")) result.httpIsSuccess = data.httpIsSuccess; + if (HasProperty(data, "processId")) result.processId = data.processId; + if (HasProperty(data, "scriptProcessed")) result.scriptProcessed = data.scriptProcessed; + if (HasProperty(data, "scriptProcessedAt")) result.scriptProcessedAt = data.scriptProcessedAt; + if (HasProperty(data, "crossWorkflowCompleted")) result.crossWorkflowCompleted = data.crossWorkflowCompleted; + if (HasProperty(data, "crossWorkflowAt")) result.crossWorkflowAt = data.crossWorkflowAt; + if (HasProperty(data, "startFlowCompleted")) result.startFlowCompleted = data.startFlowCompleted; + if (HasProperty(data, "startFlowIsSuccess")) result.startFlowIsSuccess = data.startFlowIsSuccess; + if (HasProperty(data, "startedInstanceId")) result.startedInstanceId = data.startedInstanceId; + if (HasProperty(data, "getInstanceDataCompleted")) result.getInstanceDataCompleted = data.getInstanceDataCompleted; + if (HasProperty(data, "getInstanceDataIsSuccess")) result.getInstanceDataIsSuccess = data.getInstanceDataIsSuccess; + if (HasProperty(data, "remoteInstanceData")) result.remoteInstanceData = data.remoteInstanceData; + + result.taskResults = new ExpandoObject(); + if (HasProperty(data, "taskResults")) + { + if (HasProperty(data.taskResults, "notification")) result.taskResults.notification = data.taskResults.notification; + if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; + if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; + if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; + if (HasProperty(data.taskResults, "humanTask")) result.taskResults.humanTask = data.taskResults.humanTask; + } + + result.taskResults.triggerTransition = new ExpandoObject(); + result.taskResults.triggerTransition.completed = true; + result.taskResults.triggerTransition.executedAt = DateTime.UtcNow.ToString("o"); + + LogInformation("TriggerTransitionMapping completed"); + return Task.FromResult(new ScriptResponse { Data = result }); + } +} diff --git a/core/Workflows/task-execution/task-execution-test-workflow.json b/core/Workflows/task-execution/task-execution-test-workflow.json index ded4160..e3dce52 100644 --- a/core/Workflows/task-execution/task-execution-test-workflow.json +++ b/core/Workflows/task-execution/task-execution-test-workflow.json @@ -11,6 +11,11 @@ "script-task", "start-flow-task", "get-instance-data-task", + "notification-task", + "trigger-transition-task", + "subprocess-task", + "get-instances-task", + "human-task", "task-ordering", "on-entries" ], @@ -56,7 +61,7 @@ "flow": "sys-tasks" }, "mapping": { - "location": "./src/InitTaskTestMapping.csx", + "location": "./src/task-execution-test-workflow/InitTaskTestMapping.csx", "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEluaXRUYXNrVGVzdE1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGVzdElkID0gR3VpZC5OZXdHdWlkKCkuVG9TdHJpbmcoKTsKICAgICAgICByZXN1bHQuc3RhcnRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkluaXRUYXNrVGVzdE1hcHBpbmc6IHRhc2sgZXhlY3V0aW9uIHRlc3QgaW5pdGlhbGl6ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } @@ -90,7 +95,7 @@ "flow": "sys-tasks" }, "mapping": { - "location": "./src/HttpProcessMapping.csx", + "location": "./src/task-execution-test-workflow/HttpProcessMapping.csx", "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBIdHRwUHJvY2Vzc01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgYXN5bmMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGh0dHBUYXNrID0gKHRhc2sgYXMgSHR0cFRhc2spITsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBzdHJpbmcgYmFzZVVybCA9IEdldENvbmZpZ1ZhbHVlKCJNb2NrbGFiQmFzZVVybCIpOwogICAgICAgIGh0dHBUYXNrLlVybCA9IGh0dHBUYXNrLlVybC5SZXBsYWNlKCJ7TW9ja2xhYkJhc2VVcmx9IiwgYmFzZVVybCk7CiAgICAgICAgdmFyIGJvZHkgPSBuZXcgeyBzb3VyY2UgPSAidGFzay1leGVjdXRpb24tdGVzdCIsIHRpbWVzdGFtcCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpIH07CiAgICAgICAgaHR0cFRhc2suU2V0Qm9keShib2R5KTsKICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlKCk7CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIHZhciB0YXNrUmVzcG9uc2UgPSBjb250ZXh0LkJvZHk7CiAgICAgICAgdmFyIHJlc3BvbnNlQm9keSA9IHRhc2tSZXNwb25zZS5kYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSB0YXNrUmVzcG9uc2Uuc3RhdHVzQ29kZTsKICAgICAgICByZXN1bHQuaHR0cElzU3VjY2VzcyA9IHRhc2tSZXNwb25zZS5pc1N1Y2Nlc3M7CiAgICAgICAgaWYgKHJlc3BvbnNlQm9keSAhPSBudWxsICYmIEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgInByb2Nlc3NJZCIpKQogICAgICAgICAgICByZXN1bHQucHJvY2Vzc0lkID0gcmVzcG9uc2VCb2R5LnByb2Nlc3NJZDsKICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkh0dHBQcm9jZXNzTWFwcGluZyBjb21wbGV0ZWQsIHN0YXR1cz17dGFza1Jlc3BvbnNlLnN0YXR1c0NvZGV9Iik7CiAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfTsKICAgIH0KfQo=" } } @@ -113,7 +118,7 @@ } ], "rule": { - "location": "./src/AlwaysTrueRule.csx", + "location": "./src/shared/AlwaysTrueRule.csx", "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" }, "onExecutionTasks": [] @@ -147,7 +152,7 @@ "flow": "sys-tasks" }, "mapping": { - "location": "./src/ScriptProcessMapping.csx", + "location": "./src/task-execution-test-workflow/ScriptProcessMapping.csx", "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFNjcmlwdFByb2Nlc3NNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlNjcmlwdFByb2Nlc3NNYXBwaW5nIGNvbXBsZXRlZCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" } } @@ -170,7 +175,7 @@ } ], "rule": { - "location": "./src/AlwaysTrueRule.csx", + "location": "./src/shared/AlwaysTrueRule.csx", "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" }, "onExecutionTasks": [] @@ -204,7 +209,7 @@ "flow": "sys-tasks" }, "mapping": { - "location": "./src/CrossWorkflowMapping.csx", + "location": "./src/task-execution-test-workflow/CrossWorkflowMapping.csx", "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIENyb3NzV29ya2Zsb3dNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInNjcmlwdFByb2Nlc3NlZCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkID0gZGF0YS5zY3JpcHRQcm9jZXNzZWQ7CiAgICAgICAgcmVzdWx0LmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC5jcm9zc1dvcmtmbG93QXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKICAgICAgICBMb2dJbmZvcm1hdGlvbigiQ3Jvc3NXb3JrZmxvd01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } @@ -227,7 +232,7 @@ } ], "rule": { - "location": "./src/AlwaysTrueRule.csx", + "location": "./src/shared/AlwaysTrueRule.csx", "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" }, "onExecutionTasks": [] @@ -261,7 +266,7 @@ "flow": "sys-tasks" }, "mapping": { - "location": "./src/StartFlowMapping.csx", + "location": "./src/task-execution-test-workflow/StartFlowMapping.csx", "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBTdGFydEZsb3dNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBzdGFydFRhc2sgPSAodGFzayBhcyBTdGFydFRhc2spITsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKCiAgICAgICAgdmFyIGJvZHkgPSBuZXcKICAgICAgICB7CiAgICAgICAgICAgIHNvdXJjZSA9ICJ0YXNrLWV4ZWN1dGlvbi10ZXN0IiwKICAgICAgICAgICAgcGFyZW50SW5zdGFuY2VJZCA9IGNvbnRleHQuSW5zdGFuY2UuSWQKICAgICAgICB9OwogICAgICAgIHN0YXJ0VGFzay5TZXRCb2R5KGJvZHkpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU3RhcnRGbG93TWFwcGluZyBJbnB1dEhhbmRsZXI6IGJvZHkgc2V0IGZvciB0YXJnZXQgd29ya2Zsb3ciKTsKICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlKCk7CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIHZhciB0YXNrUmVzcG9uc2UgPSBjb250ZXh0LkJvZHk7CiAgICAgICAgdmFyIHJlc3BvbnNlQm9keSA9IHRhc2tSZXNwb25zZS5kYXRhOwoKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCIpKSByZXN1bHQuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZDsKCiAgICAgICAgcmVzdWx0LnN0YXJ0Rmxvd0NvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnN0YXJ0Rmxvd0lzU3VjY2VzcyA9IHRhc2tSZXNwb25zZS5pc1N1Y2Nlc3M7CgogICAgICAgIGlmIChyZXNwb25zZUJvZHkgIT0gbnVsbCAmJiBIYXNQcm9wZXJ0eShyZXNwb25zZUJvZHksICJpbnN0YW5jZUlkIikpCiAgICAgICAgICAgIHJlc3VsdC5zdGFydGVkSW5zdGFuY2VJZCA9IHJlc3BvbnNlQm9keS5pbnN0YW5jZUlkOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigkIlN0YXJ0Rmxvd01hcHBpbmcgT3V0cHV0SGFuZGxlcjogaXNTdWNjZXNzPXt0YXNrUmVzcG9uc2UuaXNTdWNjZXNzfSIpOwogICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH07CiAgICB9Cn0K" } } @@ -284,7 +289,7 @@ } ], "rule": { - "location": "./src/AlwaysTrueRule.csx", + "location": "./src/shared/AlwaysTrueRule.csx", "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" }, "onExecutionTasks": [] @@ -318,7 +323,7 @@ "flow": "sys-tasks" }, "mapping": { - "location": "./src/GetInstanceDataMapping.csx", + "location": "./src/task-execution-test-workflow/GetInstanceDataMapping.csx", "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBHZXRJbnN0YW5jZURhdGFNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBnZXREYXRhVGFzayA9ICh0YXNrIGFzIEdldEluc3RhbmNlRGF0YVRhc2spITsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKQogICAgICAgIHsKICAgICAgICAgICAgdmFyIGluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkPy5Ub1N0cmluZygpOwogICAgICAgICAgICBnZXREYXRhVGFzay5TZXRJbnN0YW5jZShpbnN0YW5jZUlkKTsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oJCJHZXRJbnN0YW5jZURhdGFNYXBwaW5nIElucHV0SGFuZGxlcjogdGFyZ2V0IGluc3RhbmNlSWQ9e2luc3RhbmNlSWR9Iik7CiAgICAgICAgfQoKICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlKCk7CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIHZhciB0YXNrUmVzcG9uc2UgPSBjb250ZXh0LkJvZHk7CiAgICAgICAgdmFyIHJlc3BvbnNlQm9keSA9IHRhc2tSZXNwb25zZS5kYXRhOwoKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCIpKSByZXN1bHQuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0NvbXBsZXRlZCIpKSByZXN1bHQuc3RhcnRGbG93Q29tcGxldGVkID0gZGF0YS5zdGFydEZsb3dDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwoKICAgICAgICByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gdGFza1Jlc3BvbnNlLmlzU3VjY2VzczsKCiAgICAgICAgaWYgKHJlc3BvbnNlQm9keSAhPSBudWxsKQogICAgICAgICAgICByZXN1bHQucmVtb3RlSW5zdGFuY2VEYXRhID0gcmVzcG9uc2VCb2R5OwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkdldEluc3RhbmNlRGF0YU1hcHBpbmcgT3V0cHV0SGFuZGxlcjogaXNTdWNjZXNzPXt0YXNrUmVzcG9uc2UuaXNTdWNjZXNzfSIpOwogICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH07CiAgICB9Cn0K" } } @@ -326,28 +331,309 @@ "onExits": [], "transitions": [ { - "key": "auto-to-completed", - "target": "completed-state", + "key": "auto-to-notification", + "target": "notification-state", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to Notification" + }, + { + "language": "tr-TR", + "label": "Bildirime Otomatik Gecis" + } + ], + "rule": { + "location": "./src/shared/AlwaysTrueRule.csx", + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" + }, + "onExecutionTasks": [] + } + ] + }, + { + "key": "notification-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Notification Task" + }, + { + "language": "tr-TR", + "label": "Bildirim Gorevi" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "notification-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/task-execution-test-workflow/NotificationMapping.csx", + "code": "" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "auto-to-trigger-transition", + "target": "trigger-transition-state", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to Trigger Transition" + }, + { + "language": "tr-TR", + "label": "Trigger Transition'a Otomatik" + } + ], + "rule": { + "location": "./src/shared/AlwaysTrueRule.csx", + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" + }, + "onExecutionTasks": [] + } + ] + }, + { + "key": "trigger-transition-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Trigger Transition Task" + }, + { + "language": "tr-TR", + "label": "Gecis Tetikleme Gorevi" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "trigger-transition-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/task-execution-test-workflow/TriggerTransitionMapping.csx", + "code": "" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "auto-to-subprocess", + "target": "subprocess-state", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to SubProcess" + }, + { + "language": "tr-TR", + "label": "SubProcess'e Otomatik" + } + ], + "rule": { + "location": "./src/shared/AlwaysTrueRule.csx", + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" + }, + "onExecutionTasks": [] + } + ] + }, + { + "key": "subprocess-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "SubProcess Task" + }, + { + "language": "tr-TR", + "label": "Alt Surec Gorevi" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "subprocess-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/task-execution-test-workflow/SubProcessMapping.csx", + "code": "" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "auto-to-get-instances", + "target": "get-instances-state", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to GetInstances" + }, + { + "language": "tr-TR", + "label": "GetInstances'a Otomatik" + } + ], + "rule": { + "location": "./src/shared/AlwaysTrueRule.csx", + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" + }, + "onExecutionTasks": [] + } + ] + }, + { + "key": "get-instances-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "GetInstances Task" + }, + { + "language": "tr-TR", + "label": "Instance Listesi Gorevi" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "get-instances-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/task-execution-test-workflow/GetInstancesMapping.csx", + "code": "" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "auto-to-human-task", + "target": "human-task-state", "triggerType": 1, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Auto to Completed" + "label": "Auto to Human Task" }, { "language": "tr-TR", - "label": "Tamamlanmaya Otomatik Gecis" + "label": "Insan Gorevine Otomatik" } ], "rule": { - "location": "./src/AlwaysTrueRule.csx", + "location": "./src/shared/AlwaysTrueRule.csx", "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" }, "onExecutionTasks": [] } ] }, + { + "key": "human-task-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Human Task (Approval)" + }, + { + "language": "tr-TR", + "label": "Insan Gorevi (Onay)" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "human-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/task-execution-test-workflow/HumanTaskMapping.csx", + "code": "" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "approve-human-task", + "target": "completed-state", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Approve Human Task" + }, + { + "language": "tr-TR", + "label": "Insan Gorevini Onayla" + } + ], + "onExecutionTasks": [] + } + ] + }, { "key": "completed-state", "stateType": 3, diff --git a/core/Workflows/task-execution/task-target-workflow.json b/core/Workflows/task-execution/task-target-workflow.json index 7a1f9f3..a30e195 100644 --- a/core/Workflows/task-execution/task-target-workflow.json +++ b/core/Workflows/task-execution/task-target-workflow.json @@ -80,7 +80,7 @@ } ], "rule": { - "location": "./src/AlwaysTrueRule.csx", + "location": "./src/shared/AlwaysTrueRule.csx", "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" }, "onExecutionTasks": [] diff --git a/doc/integration-test-documentation.md b/doc/integration-test-documentation.md index 66e9ea3..19db4f4 100644 --- a/doc/integration-test-documentation.md +++ b/doc/integration-test-documentation.md @@ -10,14 +10,15 @@ Bu dokuman, `core` projesindeki tum integration test workflow'larini, icerdikler |---|------|----------|---------------------------| | 1 | Lifecycle & Transitions | `lifecycle-transitions-test-workflow` | State tipleri, transition tipleri, on-entries/exits, timer, cancel, **exit** (`attributes.exit`), **schedule iptal / timer reschedule**, **queryRoles** ve **transition `roles` (yalnizca state fonksiyonu listeleme filtresi)** | | 2 | SubFlow & SubProcess | `subflow-orchestration-parent` + child + grandchild | Parent-child-grandchild zinciri, parent shared transitions, **child shared transition**, **cancel cascade** (child/grandchild **cancelled** final state'leri), **effective state**, **updateData (SubFlow)** | -| 3 | Task Execution | `task-execution-test-workflow` + `task-target-workflow` | HTTP/Script/StartFlow/GetInstanceData task tipleri | +| 3 | Task Execution | `task-execution-test-workflow` + `task-target-workflow` + `extended-tasks-test-workflow` | HTTP/Script/StartFlow/GetInstanceData/Notification/TriggerTransition/SubProcess/GetInstances/Human; Dapr HTTP/Service/Binding/PubSub (`extended-tasks-test-workflow`); Mocklab + Dapr YAML | | 4 | Error Boundary | `error-boundary-test-workflow` | Retry, Ignore, **Rollback**, **Log**, **Notify** (action:4) aksiyonlari, retry policy, priority, **timeoutPolicy** (onTimeout), **errorHandlerRule** (errorTypes/errorCodes) | | 5 | View, Function & Extension | `view-function-extension-test-workflow` | View tipleri, display modlari, function, extension, wizard state, **features** referansi | | 6 | Schema & Data | `schema-data-test-workflow` | Master schema, transition schema, updateData, field roles | | 7 | Instance Management | `instance-management-test-workflow` | Filtering, pagination, sorting, timeout, idempotent start, **subType 4/5/6** (suspended/busy/human) | -| 8 | Extended Tasks | `extended-tasks-test-workflow` | Dapr HTTP/Binding/Service/PubSub, notification, trigger-transition, subprocess, get-instances, human-task; Dapr bilesen konfigurasyonu | -| 9 | Flow Types | `core-flow-test` + `subprocess-flow-test` | **Core (C)** ve **SubProcess (P)** workflow tipleri | -| 10 | Version Consistency | `version-consistency-test-workflow` (v1.0.0 + v2.0.0) | Workflow versiyon degisiminde mevcut instance'larin kendi versiyonlariyla devam etmesi | +| 8 | Flow Types | `core-flow-test` + `subprocess-flow-test` | **Core (C)** ve **SubProcess (P)** workflow tipleri | +| 9 | Version Consistency | `version-consistency-test-workflow` (v1.0.0 + v2.0.0) | Workflow versiyon degisiminde mevcut instance'larin kendi versiyonlariyla devam etmesi | + +**Grup numaralari:** Eski **Grup 8 (Extended Tasks)** icerigi **Grup 3 (Task Execution)** altinda birlestirilmistir (`extended-tasks-test-workflow` artik Grup 3 kapsaminda). Matris ve tablolarda **G8 / Grup 8** = Flow Types, **G9 / Grup 9** = Version Consistency. --- @@ -319,15 +320,20 @@ subflow-orchestration-parent (F) --- -## Grup 3: Task Execution & Scripting +## Grup 3: Task Execution (runtime gorevleri + Dapr dis servis) **Workflow'lar:** -- `task-execution-test-workflow` (type: F) - Ana test workflow'u -- `task-target-workflow` (type: F) - Cross-workflow test icin hedef +- `task-execution-test-workflow` (type: F) — Ana test workflow'u (HTTP, Script, StartFlow, GetInstanceData, Notification, TriggerTransition, SubProcess, GetInstances, Human task + manuel onay) +- `task-target-workflow` (type: F) — Cross-workflow / StartFlow / GetInstanceData / TriggerTransition / SubProcess / GetInstances icin hedef +- `extended-tasks-test-workflow` (type: F) — **Sadece Dapr** ile dis servis cagrisi: HTTP invoke, Service, Binding, PubSub (tek zincir, `api-tests/task-execution/extended-tasks-test-workflow.http`) -**Amac:** Farkli task tiplerini (HTTP, Script, StartFlow, GetInstanceData) ve task siralama mekanizmasini test eder. +**Amac:** Tum **runtime-ici** gorev tipleri ile **Dapr tabanli** gorev tiplerini ayri workflow'larda dogrular; task siralama (`onEntries` order), `context.Body.data`, cross-workflow iletisim ve Dapr bilesen YAML (`etc/dapr/components/`) ile uyumu kapsar. -### Agac Yapisi +**CSX klasorleri:** `Workflows/task-execution/src/task-execution-test-workflow/` (`task-execution-test-workflow.json` mapping'leri), `.../src/extended-tasks-test-workflow/` (`extended-tasks-test-workflow.json`), `.../src/shared/AlwaysTrueRule.csx` (otomatik gecis kurallari; `task-target-workflow` dahil). + +**Task JSON klasorleri:** `Tasks/task-execution/task-execution-test-workflow/` (ana workflow + hedef workflow'un kullandigi task'lar), `Tasks/task-execution/extended-tasks-test-workflow/` (Dapr task'lari). + +### Agac Yapisi — `task-execution-test-workflow` (F) ``` task-execution-test-workflow (F) @@ -337,82 +343,115 @@ task-execution-test-workflow (F) │ └── task-exec-script-task + InitTaskTestMapping.csx │ ├── http-task-state (Initial, stateType:1) -│ ├── onEntries: -│ │ └── http-process-task (type:6) + HttpProcessMapping.csx -│ └── Transitions: -│ └── auto-to-script-processing (Auto) → script-processing-state +│ ├── onEntries: http-process-task (type:6) + HttpProcessMapping.csx +│ └── Transitions: auto-to-script-processing → script-processing-state │ -├── script-processing-state (Intermediate, stateType:2) -│ ├── onEntries: -│ │ └── task-exec-script-task (type:7) + ScriptProcessMapping.csx -│ └── Transitions: -│ └── auto-to-cross-workflow (Auto) → cross-workflow-state +├── script-processing-state +│ ├── onEntries: task-exec-script-task (type:7) + ScriptProcessMapping.csx +│ └── Transitions: auto-to-cross-workflow → cross-workflow-state │ -├── cross-workflow-state (Intermediate, stateType:2) -│ ├── onEntries: -│ │ └── task-exec-script-task (type:7) + CrossWorkflowMapping.csx -│ └── Transitions: -│ └── auto-to-start-flow (Auto) → start-flow-state +├── cross-workflow-state +│ ├── onEntries: task-exec-script-task + CrossWorkflowMapping.csx +│ └── Transitions: auto-to-start-flow → start-flow-state │ -├── start-flow-state (Intermediate, stateType:2) -│ ├── onEntries: -│ │ └── start-flow-task (type:11) + StartFlowMapping.csx -│ └── Transitions: -│ └── auto-to-get-instance-data (Auto) → get-instance-data-state +├── start-flow-state +│ ├── onEntries: start-flow-task (type:11) + StartFlowMapping.csx +│ └── Transitions: auto-to-get-instance-data → get-instance-data-state │ -├── get-instance-data-state (Intermediate, stateType:2) -│ ├── onEntries: -│ │ └── get-instance-data-task (type:13) + GetInstanceDataMapping.csx -│ └── Transitions: -│ └── auto-to-completed (Auto) → completed-state +├── get-instance-data-state +│ ├── onEntries: get-instance-data-task (type:13) + GetInstanceDataMapping.csx +│ └── Transitions: auto-to-notification → notification-state +│ +├── notification-state +│ ├── onEntries: notification-task (type:10) + NotificationMapping.csx +│ └── Transitions: auto-to-trigger-transition → trigger-transition-state +│ +├── trigger-transition-state +│ ├── onEntries: trigger-transition-task (type:12) + TriggerTransitionMapping.csx +│ └── Transitions: auto-to-subprocess → subprocess-state +│ +├── subprocess-state +│ ├── onEntries: subprocess-task (type:14) + SubProcessMapping.csx +│ └── Transitions: auto-to-get-instances → get-instances-state +│ +├── get-instances-state +│ ├── onEntries: get-instances-task (type:15) + GetInstancesMapping.csx +│ └── Transitions: auto-to-human-task → human-task-state +│ +├── human-task-state +│ ├── onEntries: human-task (type:5) + HumanTaskMapping.csx +│ └── Transitions: approve-human-task (Manual) → completed-state │ └── completed-state (Final/Success, stateType:3, subType:1) task-target-workflow (F) │ ├── [startTransition] start-target → target-initial +├── target-initial → auto-complete-target → target-completed +└── target-completed +``` + +### Agac Yapisi — `extended-tasks-test-workflow` (F, sadece Dapr) + +``` +extended-tasks-test-workflow (F) │ -├── target-initial (Initial, stateType:1) -│ └── Transitions: -│ └── auto-complete-target (Auto) → target-completed -│ └── rule: AlwaysTrueRule.csx +├── [startTransition] start-extended-tasks → init-state │ -└── target-completed (Final/Success, stateType:3, subType:1) +├── init-state (Initial) +│ ├── onEntries: script-task (Tasks/lifecycle-transitions/script-task.json) + InitExtendedTaskMapping.csx +│ └── Transitions: auto-to-dapr-http → dapr-http-state +│ +├── dapr-http-state → dapr-service-state → dapr-binding-state → dapr-pubsub-state +│ (sirasiyla type 1, 3, 2, 4 Dapr task'leri + Dapr*Mapping.csx) +│ +└── completed-state (Final; pubsub sonrasi otomatik tamamlanma, manuel human task yok) ``` ### Test Edilen Ozellikler | Ozellik | Nasil Test Edilir | Ilgili Eleman | |---------|-------------------|---------------| -| HTTP Task (type:6) | Mockoon'a HTTP istegi gonderme | http-process-task + HttpProcessMapping | -| Script Task (type:7) | C# script ile data donusumu | task-exec-script-task + ScriptProcessMapping | -| StartFlow Task (type:11) | Baska bir workflow baslatma | start-flow-task + StartFlowMapping | -| GetInstanceData Task (type:13) | Baska workflow'un datasini okuma | get-instance-data-task + GetInstanceDataMapping | -| HttpTask cast + SetBody | HTTP task'a body set etme | HttpProcessMapping.csx InputHandler | -| StartTask cast + SetBody | Start task'a body set etme | StartFlowMapping.csx InputHandler | -| GetInstanceDataTask cast + SetInstance | Hedef instance belirleme | GetInstanceDataMapping.csx InputHandler | -| context.Body.data | HTTP/Task response okuma | OutputHandler'larda context.Body.data | -| GetConfigValue | Vault'tan config degeri okuma | HttpProcessMapping (MocklabBaseUrl) | -| Task siralama (onEntries order) | Her state'te order:1 task | Tum onEntries | -| Cross-workflow iletisim | Bir workflow baska bir workflow baslatir ve datasini okur | start-flow → get-instance-data zinciri | - -### Kullanilan Bilesenler +| HTTP Task (type:6) | Mocklab HTTP | http-process-task + HttpProcessMapping | +| Script Task (type:7) | C# script data | task-exec-script-task + ScriptProcessMapping | +| StartFlow Task (type:11) | Hedef workflow baslatma | start-flow-task + StartFlowMapping | +| GetInstanceData Task (type:13) | Baska instance datasi | get-instance-data-task + GetInstanceDataMapping | +| Notification Task (type:10) | Bildirim metadata | notification-task + NotificationMapping | +| TriggerTransition Task (type:12) | Hedef instance'da transition | trigger-transition-task + TriggerTransitionMapping | +| SubProcess Task (type:14) | Alt surec baslatma | subprocess-task + SubProcessMapping | +| GetInstances Task (type:15) | Instance listesi | get-instances-task + GetInstancesMapping | +| Human Task (type:5) | Manuel `approve-human-task` | human-task + HumanTaskMapping | +| Dapr HTTP (1) / Service (3) / Binding (2) / PubSub (4) | Sidecar + YAML | `extended-tasks-test-workflow` + dapr-*-task | +| HttpTask / StartTask / GetInstanceDataTask cast | InputHandler | Ilgili mapping.csx | +| context.Body.data | Task yaniti | OutputHandler'lar | +| GetConfigValue | MocklabBaseUrl | HttpProcessMapping | +| Task siralama | onEntries order | Tum state'ler | +| Cross-workflow | StartFlow + GetInstanceData + hedef workflow | task-target-workflow | + +### Kullanilan Bilesenler (ozet) | Tip | Anahtar | Tip Kodu | Dosya | |-----|---------|----------|-------| -| Task | `http-process-task` | type:6 (HTTP) | Tasks/task-execution/http-process-task.json | -| Task | `task-exec-script-task` | type:7 (Script) | Tasks/task-execution/task-exec-script-task.json | -| Task | `start-flow-task` | type:11 (StartFlow) | Tasks/task-execution/start-flow-task.json | -| Task | `get-instance-data-task` | type:13 (GetInstanceData) | Tasks/task-execution/get-instance-data-task.json | -| CSX | InitTaskTestMapping | | Workflows/task-execution/src/InitTaskTestMapping.csx | -| CSX | HttpProcessMapping | | Workflows/task-execution/src/HttpProcessMapping.csx | -| CSX | ScriptProcessMapping | | Workflows/task-execution/src/ScriptProcessMapping.csx | -| CSX | CrossWorkflowMapping | | Workflows/task-execution/src/CrossWorkflowMapping.csx | -| CSX | StartFlowMapping | | Workflows/task-execution/src/StartFlowMapping.csx | -| CSX | GetInstanceDataMapping | | Workflows/task-execution/src/GetInstanceDataMapping.csx | -| CSX | AlwaysTrueRule | | Workflows/task-execution/src/AlwaysTrueRule.csx | +| Task | `http-process-task` | 6 | Tasks/task-execution/task-execution-test-workflow/http-process-task.json | +| Task | `task-exec-script-task` | 7 | Tasks/task-execution/task-execution-test-workflow/task-exec-script-task.json | +| Task | `start-flow-task` | 11 | Tasks/task-execution/task-execution-test-workflow/start-flow-task.json | +| Task | `get-instance-data-task` | 13 | Tasks/task-execution/task-execution-test-workflow/get-instance-data-task.json | +| Task | `notification-task` | 10 | Tasks/task-execution/task-execution-test-workflow/notification-task.json | +| Task | `trigger-transition-task` | 12 | Tasks/task-execution/task-execution-test-workflow/trigger-transition-task.json | +| Task | `subprocess-task` | 14 | Tasks/task-execution/task-execution-test-workflow/subprocess-task.json | +| Task | `get-instances-task` | 15 | Tasks/task-execution/task-execution-test-workflow/get-instances-task.json | +| Task | `human-task` | 5 | Tasks/task-execution/task-execution-test-workflow/human-task.json | +| Task | `dapr-http-task` | 1 | Tasks/task-execution/extended-tasks-test-workflow/dapr-http-task.json | +| Task | `dapr-service-task` | 3 | Tasks/task-execution/extended-tasks-test-workflow/dapr-service-task.json | +| Task | `dapr-binding-task` | 2 | Tasks/task-execution/extended-tasks-test-workflow/dapr-binding-task.json | +| Task | `dapr-pubsub-task` | 4 | Tasks/task-execution/extended-tasks-test-workflow/dapr-pubsub-task.json | +| Task | `script-task` (paylasimli) | 7 | Tasks/lifecycle-transitions/script-task.json | +| CSX | `task-execution-test-workflow` mapping'leri | `Workflows/task-execution/src/task-execution-test-workflow/*.csx` | +| CSX | `extended-tasks-test-workflow` mapping'leri | `Workflows/task-execution/src/extended-tasks-test-workflow/*.csx` | +| CSX | Ortak auto-transition rule | `Workflows/task-execution/src/shared/AlwaysTrueRule.csx` | + +**HTTP / Postman:** `api-tests/task-execution/task-execution-test-workflow.http` (ana zincir + human onay), `api-tests/task-execution/extended-tasks-test-workflow.http` (Dapr-only). Mini Postman: `postman-task-execution.json`, `postman-extended-tasks.json` (`api-tests/task-execution/` klasorunde). ---- ## Grup 4: Error Boundary @@ -767,136 +806,7 @@ instance-management-test-workflow (F) --- -## Grup 8: Extended Tasks (Dapr ve Genisletilmis Gorev Tipleri) - -**Workflow:** `extended-tasks-test-workflow` (type: F) - -**Dosya:** `Workflows/extended-tasks/extended-tasks-test-workflow.json` - -**Amac:** Dapr tabanli gorev tipleri (HTTP, Service, Binding, PubSub), bildirim, baska instance uzerinde gecis tetikleme, alt surec baslatma, instance listesi sorgulama ve insan gorevi (manuel onay) akislarini tek workflow zincirinde dogrular. Baslangic state'inde **lifecycle-transitions** modulundeki **`script-task`** tanimi yeniden kullanilir; mapping bu workflow'a ozel **`InitExtendedTaskMapping.csx`** dosyasidir. - -### Gorev Tipleri (`Tasks/extended-tasks/`) - -| Anahtar | type (kod) | Aciklama | -|---------|------------|----------| -| `dapr-http-task` | 1 | Dapr HTTP invoke | -| `dapr-binding-task` | 2 | Dapr output binding | -| `dapr-service-task` | 3 | Dapr service invocation | -| `dapr-pubsub-task` | 4 | Dapr pub/sub publish | -| `human-task` | 5 | Insan onayi / manuel gorev | -| `notification-task` | 10 | Dis bildirim (HTTP vb.) | -| `trigger-transition-task` | 12 | Hedef instance'da transition tetikleme | -| `subprocess-task` | 14 | Alt surec / bagli flow calistirma | -| `get-instances-task` | 15 | Instance listesi filtreleme | - -### Agac Yapisi - -``` -extended-tasks-test-workflow (F) -│ -├── [startTransition] start-extended-tasks → init-state -│ └── onExecutionTasks: (bos) -│ -├── init-state (Initial, stateType:1) -│ ├── onEntries: -│ │ └── script-task (lifecycle-transitions tanimi) + InitExtendedTaskMapping.csx -│ └── Transitions: -│ └── auto-to-dapr-http (Auto) → dapr-http-state -│ └── rule: AlwaysTrueRule.csx -│ -├── dapr-http-state (Intermediate, stateType:2) -│ ├── onEntries: dapr-http-task (type:1) + DaprHttpMapping.csx -│ └── Transitions: auto-to-dapr-service → dapr-service-state (AlwaysTrueRule) -│ -├── dapr-service-state (Intermediate, stateType:2) -│ ├── onEntries: dapr-service-task (type:3) + DaprServiceMapping.csx -│ └── Transitions: auto-to-dapr-binding → dapr-binding-state -│ -├── dapr-binding-state (Intermediate, stateType:2) -│ ├── onEntries: dapr-binding-task (type:2) + DaprBindingMapping.csx -│ └── Transitions: auto-to-dapr-pubsub → dapr-pubsub-state -│ -├── dapr-pubsub-state (Intermediate, stateType:2) -│ ├── onEntries: dapr-pubsub-task (type:4) + DaprPubSubMapping.csx -│ └── Transitions: auto-to-notification → notification-state -│ -├── notification-state (Intermediate, stateType:2) -│ ├── onEntries: notification-task (type:10) + NotificationMapping.csx -│ └── Transitions: auto-to-trigger-transition → trigger-transition-state -│ -├── trigger-transition-state (Intermediate, stateType:2) -│ ├── onEntries: trigger-transition-task (type:12) + TriggerTransitionMapping.csx -│ └── Transitions: auto-to-subprocess → subprocess-state -│ -├── subprocess-state (Intermediate, stateType:2) -│ ├── onEntries: subprocess-task (type:14) + SubProcessMapping.csx -│ └── Transitions: auto-to-get-instances → get-instances-state -│ -├── get-instances-state (Intermediate, stateType:2) -│ ├── onEntries: get-instances-task (type:15) + GetInstancesMapping.csx -│ └── Transitions: auto-to-human-task → human-task-state -│ -├── human-task-state (Intermediate, stateType:2) -│ ├── onEntries: human-task (type:5) + HumanTaskMapping.csx -│ └── Transitions: -│ └── approve-human-task (Manual) → completed-state -│ -└── completed-state (Final/Success, stateType:3, subType:1) -``` - -### CSX Dosyalari (`Workflows/extended-tasks/src/`) - -| Dosya | Rol | -|-------|-----| -| InitExtendedTaskMapping.csx | IMapping — init-state onEntries | -| DaprHttpMapping.csx | IMapping | -| DaprServiceMapping.csx | IMapping | -| DaprBindingMapping.csx | IMapping | -| DaprPubSubMapping.csx | IMapping | -| NotificationMapping.csx | IMapping | -| TriggerTransitionMapping.csx | IMapping | -| SubProcessMapping.csx | IMapping | -| GetInstancesMapping.csx | IMapping | -| HumanTaskMapping.csx | IMapping | -| AlwaysTrueRule.csx | IConditionMapping — otomatik gecis kurallari | - -### Test Edilen Ozellikler (ozet) - -| Ozellik | Nasil Test Edilir | Ilgili Eleman | -|---------|-------------------|---------------| -| Dapr HTTP task | Sidecar uzerinden uzaktan HTTP cagri | dapr-http-task, DaprHttpMapping | -| Dapr Service task | App-id ile service invocation | dapr-service-task | -| Dapr Binding task | Tanimli output binding | dapr-binding-task, test-binding.yaml | -| Dapr PubSub task | Pub/sub mesaj yayini | dapr-pubsub-task, test-pubsub.yaml | -| Notification task | Mock HTTP bildirim ucu | notification-task | -| Trigger transition task | Baska instance transition | trigger-transition-task | -| Subprocess task | Bagli workflow calistirma | subprocess-task | -| Get instances task | Filtreli instance listesi | get-instances-task | -| Human task | Manuel onay ile tamamlama | human-task, approve-human-task | -| Script task paylasimi | Ayni script-task JSON, farkli mapping | script-task + InitExtendedTaskMapping | - -### HTTP Test - -`Workflows/extended-tasks/extended-tasks-test-workflow.http` dosyasi ile uçtan uca senaryo ve ara kontroller calistirilabilir. - -### Kullanilan Bilesenler (task JSON yollari) - -| Tip | Anahtar | Dosya | -|-----|---------|-------| -| Task | `dapr-http-task` | Tasks/extended-tasks/dapr-http-task.json | -| Task | `dapr-binding-task` | Tasks/extended-tasks/dapr-binding-task.json | -| Task | `dapr-service-task` | Tasks/extended-tasks/dapr-service-task.json | -| Task | `dapr-pubsub-task` | Tasks/extended-tasks/dapr-pubsub-task.json | -| Task | `human-task` | Tasks/extended-tasks/human-task.json | -| Task | `notification-task` | Tasks/extended-tasks/notification-task.json | -| Task | `trigger-transition-task` | Tasks/extended-tasks/trigger-transition-task.json | -| Task | `subprocess-task` | Tasks/extended-tasks/subprocess-task.json | -| Task | `get-instances-task` | Tasks/extended-tasks/get-instances-task.json | -| Task | `script-task` (paylasimli) | Tasks/lifecycle-transitions/script-task.json | - ---- - -## Grup 9: Flow Types (Core ve SubProcess) +## Grup 8: Flow Types (Core ve SubProcess) **Workflow'lar:** - `core-flow-test` (type: C) - Core flow tipi @@ -944,7 +854,7 @@ subprocess-flow-test (P) |---------|-------------------|---------------| | Core flow tipi (type: C) | Minimal workflow publish ve calistirma | core-flow-test | | SubProcess flow tipi (type: P) | Minimal workflow publish ve calistirma | subprocess-flow-test | -| Farkli flow tiplerinin runtime uyumlulugu | Tum 4 tip: C (G9), F (G1-G8), S (G2), P (G9) | Tum gruplarda | +| Farkli flow tiplerinin runtime uyumlulugu | Tum 4 tip: C (G8), F (G1-G7, G9), S (G2), P (G8) | Tum gruplarda | ### Kullanilan Bilesenler @@ -964,7 +874,7 @@ subprocess-flow-test (P) --- -## Grup 10: Version Consistency (Versiyon Tutarliligi) +## Grup 9: Version Consistency (Versiyon Tutarliligi) **Workflow:** `version-consistency-test-workflow` (type: F) — ayni key, iki farkli versiyon (v1.0.0 ve v2.0.0) @@ -1093,8 +1003,8 @@ version-consistency-test-workflow (F, v2.0.0) — 4 state (ekstra review-state) | Dosya | Amac | |-------|------| -| `etc/dapr/components/test-binding.yaml` | Dapr output binding testleri (Grup 8) | -| `etc/dapr/components/test-pubsub.yaml` | Dapr pub/sub testleri (Grup 8) | +| `etc/dapr/components/test-binding.yaml` | Dapr output binding testleri (Grup 3, `extended-tasks-test-workflow`) | +| `etc/dapr/components/test-pubsub.yaml` | Dapr pub/sub testleri (Grup 3, `extended-tasks-test-workflow`) | | `etc/dapr/config.yaml` | Dapr genel konfigurasyon | ### Mockoon Mock Endpoint'leri @@ -1136,83 +1046,83 @@ Integration testlerde kullanilan C# script interface'leri: ## Ozellik Kapsam Matrisi -Her grubun hangi vNext ozelliklerini test ettigini gosteren matris: - -| Ozellik | G1 | G2 | G3 | G4 | G5 | G6 | G7 | G8 | G9 | G10 | -|---------|----|----|----|----|----|----|----|-----|-----|------| -| State tipleri (1/2/3) | X | X | X | X | X | X | X | X | X | X | -| State alt tipleri (1/2/3) | X | X | | X | | | X | X | | | -| **State alt tipleri (4/5/6)** | | | | | | | X | | | | -| SubFlow state (4) | | X | | | | | | | | | -| Wizard state (5) | | | | | X | | | | | | -| Manuel transition (0) | X | X | | | X | X | X | X | | X | -| Otomatik transition (1) | X | X | X | X | X | | | X | X | X | -| Zamanlanmis transition (2) | X | | | | | | | | | | -| triggerKind:10 (default) | X | | | | X | | | | | | -| onEntries | X | X | X | X | | | | X | X | X | -| onExits | X | | | | | | | | | | -| startTransition tasks | X | X | X | X | X | X | X | | X | X | -| IMapping | X | X | X | X | X | X | X | X | X | X | -| IConditionMapping | X | X | X | X | X | | | X | X | X | -| ITimerMapping | X | | | | | | | | | | -| ITransitionMapping | X | | | | | | | | | | -| ISubFlowMapping | | X | | | | | | | | | -| IOutputHandler | | | | | X | | | | | | -| HTTP Task (type:6) | | | X | X | | | | | | | -| Script Task (type:7) | X | X | X | X | X | X | X | X | X | X | -| StartFlow Task (11) | | | X | | | | | | | | -| GetInstanceData Task (13) | | | X | | | | | | | | -| **Dapr HTTP Task (1)** | | | | | | | | X | | | -| **Dapr Binding Task (2)** | | | | | | | | X | | | -| **Dapr Service Task (3)** | | | | | | | | X | | | -| **Dapr PubSub Task (4)** | | | | | | | | X | | | -| **Human Task (5)** | | | | | | | | X | | | -| **Notification Task (10)** | | | | | | | | X | | | -| **Trigger Transition Task (12)** | | | | | | | | X | | | -| **SubProcess Task (14)** | | | | | | | | X | | | -| **Get Instances Task (15)** | | | | | | | | X | | | -| Cancel transition | X | X | | | | | | | | | -| **Exit transition (`attributes.exit`)** | X | | | | | | | | | | -| **Schedule cancel (manuel)** | X | | | | | | | | | | -| **Timer reschedule (self-loop)** | X | | | | | | | | | | -| Shared transitions ($self) | | X | | | | | | | | | -| **Child-level shared transition** | | X | | | | | | | | | -| **SubFlow cancel final states (child/grandchild)** | | X | | | | | | | | | -| **Effective state (/functions/state, nested)** | | X | | | | | | | | | -| updateData ($self) | | X | | | | X | | | | | -| **UpdateData (SubFlow context)** | | X | | | | | | | | | -| Master schema | | | | | | X | | | | | -| Transition schema | | | | | | X | | | | | -| Field roles (master schema) | | | | | | X | | | | | -| **queryRoles (workflow/state)** | X | | | | | | | | | | -| **Transition roles (state fn. list filter)** | X | | | | | | | | | | -| Error boundary (task) | | | | X | | | | | | | -| Error boundary (state) | | | | X | | | | | | | -| Error boundary (workflow) | | | | X | | | | | | | -| Retry policy | | | | X | | | | | | | -| **Rollback (action:2)** | | | | X | | | | | | | -| **Log action (action:5, state)** | | | | X | | | | | | | -| **Notify action (action:4)** | | | | X | | | | | | | -| **timeoutPolicy (onTimeout)** | | | | X | | | | | | | -| **errorHandlerRule (errorTypes/errorCodes)** | | | | X | | | | | | | -| View (JSON/HTML/MD) | | | | | X | | | | | | -| Display modlari | | | | | X | | | | | | -| Function (single/multi) | | | | | X | | | | | | -| Extension (global/req) | | | | | X | | | | | | -| **Features referansi** | | | | | X | | | | | | -| Workflow timeout | | | | | | | X | | | | -| Idempotent start | X | | | | | | X | | | | -| Instance filtreleme | | | | | | | X | X | | | -| Sayfalama/Siralama | | | | | | | X | X | | | -| Cross-workflow task | | | X | | | | | | | | -| Complementary rules | X | | | | X | | | | | | -| **Dapr sidecar + bilesen YAML** | | | | | | | | X | | | -| **Flow tipi: Core (C)** | | | | | | | | | X | | -| **Flow tipi: SubProcess (P)** | | | | | | | | | X | | -| Flow tipi: Flow (F) | X | | X | X | X | X | X | X | | X | -| Flow tipi: SubFlow (S) | | X | | | | | | | | | -| **Version isolation (ayni key, farkli versiyon)** | | | | | | | | | | X | -| **Eski instance eski versiyonda kalir** | | | | | | | | | | X | +Her grubun hangi vNext ozelliklerini test ettigini gosteren matris. **Gn** sutunu Genel Bakis tablosundaki **Grup n** ile eslesir (1-9; birlestirilmis Dapr senaryosu **G3**). + +| Ozellik | G1 | G2 | G3 | G4 | G5 | G6 | G7 | G8 | G9 | +|---------|----|----|----|----|----|----|----|----|----| +| State tipleri (1/2/3) | X | X | X | X | X | X | X | X | X | +| State alt tipleri (1/2/3) | X | X | | X | | | X | | | +| **State alt tipleri (4/5/6)** | | | | | | | X | | | +| SubFlow state (4) | | X | | | | | | | | +| Wizard state (5) | | | | | X | | | | | +| Manuel transition (0) | X | X | | | X | X | X | | X | +| Otomatik transition (1) | X | X | X | X | X | | | X | X | +| Zamanlanmis transition (2) | X | | | | | | | | | +| triggerKind:10 (default) | X | | | | X | | | | | +| onEntries | X | X | X | X | | | | X | X | +| onExits | X | | | | | | | | | +| startTransition tasks | X | X | X | X | X | X | X | X | X | +| IMapping | X | X | X | X | X | X | X | X | X | +| IConditionMapping | X | X | X | X | X | | | X | X | +| ITimerMapping | X | | | | | | | | | +| ITransitionMapping | X | | | | | | | | | +| ISubFlowMapping | | X | | | | | | | | +| IOutputHandler | | | | | X | | | | | +| HTTP Task (type:6) | | | X | X | | | | | | +| Script Task (type:7) | X | X | X | X | X | X | X | X | X | +| StartFlow Task (11) | | | X | | | | | | | +| GetInstanceData Task (13) | | | X | | | | | | | +| **Dapr HTTP Task (1)** | | | X | | | | | | | +| **Dapr Binding Task (2)** | | | X | | | | | | | +| **Dapr Service Task (3)** | | | X | | | | | | | +| **Dapr PubSub Task (4)** | | | X | | | | | | | +| **Human Task (5)** | | | X | | | | | | | +| **Notification Task (10)** | | | X | | | | | | | +| **Trigger Transition Task (12)** | | | X | | | | | | | +| **SubProcess Task (14)** | | | X | | | | | | | +| **Get Instances Task (15)** | | | X | | | | | | | +| Cancel transition | X | X | | | | | | | | +| **Exit transition (`attributes.exit`)** | X | | | | | | | | | +| **Schedule cancel (manuel)** | X | | | | | | | | | +| **Timer reschedule (self-loop)** | X | | | | | | | | | +| Shared transitions ($self) | | X | | | | | | | | +| **Child-level shared transition** | | X | | | | | | | | +| **SubFlow cancel final states (child/grandchild)** | | X | | | | | | | | +| **Effective state (/functions/state, nested)** | | X | | | | | | | | +| updateData ($self) | | X | | | | X | | | | +| **UpdateData (SubFlow context)** | | X | | | | | | | | +| Master schema | | | | | | X | | | | +| Transition schema | | | | | | X | | | | +| Field roles (master schema) | | | | | | X | | | | +| **queryRoles (workflow/state)** | X | | | | | | | | | +| **Transition roles (state fn. list filter)** | X | | | | | | | | | +| Error boundary (task) | | | | X | | | | | | +| Error boundary (state) | | | | X | | | | | | +| Error boundary (workflow) | | | | X | | | | | | +| Retry policy | | | | X | | | | | | +| **Rollback (action:2)** | | | | X | | | | | | +| **Log action (action:5, state)** | | | | X | | | | | | +| **Notify action (action:4)** | | | | X | | | | | | +| **timeoutPolicy (onTimeout)** | | | | X | | | | | | +| **errorHandlerRule (errorTypes/errorCodes)** | | | | X | | | | | | +| View (JSON/HTML/MD) | | | | | X | | | | | +| Display modlari | | | | | X | | | | | +| Function (single/multi) | | | | | X | | | | | +| Extension (global/req) | | | | | X | | | | | +| **Features referansi** | | | | | X | | | | | +| Workflow timeout | | | | | | | X | | | +| Idempotent start | X | | | | | | X | | | +| Instance filtreleme | | | X | | | | X | | | +| Sayfalama/Siralama | | | X | | | | X | | | +| Cross-workflow task | | | X | | | | | | | +| Complementary rules | X | | | | X | | | | | +| **Dapr sidecar + bilesen YAML** | | | X | | | | | | | +| **Flow tipi: Core (C)** | | | | | | | | X | | +| **Flow tipi: SubProcess (P)** | | | | | | | | X | | +| Flow tipi: Flow (F) | X | | X | X | X | X | X | | X | +| Flow tipi: SubFlow (S) | | X | | | | | | | | +| **Version isolation (ayni key, farkli versiyon)** | | | | | | | | | X | +| **Eski instance eski versiyonda kalir** | | | | | | | | | X | --- diff --git a/index.js b/index.js index 91c114f..102e99d 100644 --- a/index.js +++ b/index.js @@ -35,31 +35,40 @@ function findDomainDirectory() { return pathsConfig.componentsRoot; } -// Load JSON files from a directory +// Load JSON files from a directory (recursively; basename is the map key) function loadJsonFiles(dirPath) { const files = {}; if (!fs.existsSync(dirPath)) { return files; } - - const entries = fs.readdirSync(dirPath, { withFileTypes: true }); - for (const entry of entries) { - // Skip .meta directory - if (entry.name === '.meta') { - continue; - } - - if (entry.isFile() && entry.name.endsWith('.json')) { - try { - const filePath = path.join(dirPath, entry.name); - const content = JSON.parse(fs.readFileSync(filePath, 'utf8')); - const baseName = entry.name.replace('.json', ''); - files[baseName] = content; - } catch (error) { - console.warn(`Warning: Could not load ${entry.name}: ${error.message}`); + + const walk = (currentDir) => { + const entries = fs.readdirSync(currentDir, { withFileTypes: true }); + for (const entry of entries) { + if (entry.name === '.meta') { + continue; + } + const fullPath = path.join(currentDir, entry.name); + if (entry.isDirectory()) { + walk(fullPath); + } else if (entry.isFile() && entry.name.endsWith('.json')) { + const baseName = entry.name.replace(/\.json$/i, ''); + if (Object.prototype.hasOwnProperty.call(files, baseName)) { + console.warn( + `Warning: Duplicate JSON basename "${baseName}" under ${dirPath}; keeping first, skipping ${fullPath}` + ); + continue; + } + try { + files[baseName] = JSON.parse(fs.readFileSync(fullPath, 'utf8')); + } catch (error) { + console.warn(`Warning: Could not load ${fullPath}: ${error.message}`); + } } } - } + }; + + walk(dirPath); return files; } diff --git a/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs b/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs index 6a29323..e45342c 100644 --- a/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs +++ b/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs @@ -4,24 +4,6 @@ namespace Core.IntegrationTests.Tests.LifecycleTransitionsTestWorkflow; -/* -* Bug olabilecek noktalar -* - cancel: availableIn: [] içerisine wf cli'ı yazılan state'leri error olarak -* algılıyor. Runtime tarafı nasıl bilemiyorum -* - bir state'ten başka bir state'e transtion çalıştırıldığında -* diğer state'in auto transition'u varsa ve bu otomatik çalışınca -* başka state'e geçiyorsa ve son geçilen state'in hatalı transition mapping code'u -* varsa status F oluyor ve son state geçişi gösterilmiyor. Ne demek istiyorum: -* A -> B -> C -> D -* A'dan B'ye manuel geçiş. B auto geçişi var. C'ye geçiyor problem yok gibi. -* C'nin D'ye transition'u var ve bu transition'a bağlı mapping'de problem var. -* workflow deployunda felan problem çıkmyıor. A'dan B'ye transition olduğunda -* state B'de gösteriliyor ama aslında C ile D arasındaki transition'dan dolayı uygulama -* F status'e düşüyor. workflow B durumunda F status ile duruyor kalıyor. -! Kısacası feedback yanlış oluyor. state consistency yanlış oluyor. -* -*/ - /* ! Buglar: ! Cancel From 835fadfe9a4b4111d56b2e269992695fbb71d859 Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Thu, 30 Apr 2026 12:59:08 +0300 Subject: [PATCH 05/24] human task type changed from 7 to 5. 7th task type is considered as script task and 5th task type is considered as human task type --- .../task-execution-test-workflow/human-task.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/Tasks/task-execution/task-execution-test-workflow/human-task.json b/core/Tasks/task-execution/task-execution-test-workflow/human-task.json index 26e6c73..792a823 100644 --- a/core/Tasks/task-execution/task-execution-test-workflow/human-task.json +++ b/core/Tasks/task-execution/task-execution-test-workflow/human-task.json @@ -5,11 +5,12 @@ "flow": "sys-tasks", "flowVersion": "1.0.0", "tags": ["integration-test", "task-execution", "human-task"], + "_comment": "TaskType 5 = Human Task (task-definition.schema.json). Script Task (7) degildir.", "attributes": { "type": "5", "config": { - "title": "Test Approval", - "instructions": "Approve or reject the test request", + "title": "Task Execution Integration — Human Onay", + "instructions": "Integration test human-task-state adiminda manuel onay beklenir; PATCH approve-human-task ile tamamlanir.", "assignedTo": "test-user" } } From 34aa6b92c0f94bf9feee0ec3e355cf9d102f599c Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Thu, 30 Apr 2026 15:00:01 +0300 Subject: [PATCH 06/24] human task state status F bug --- .../extended-tasks-test-workflow.http | 40 --- .../postman-extended-tasks.json | 144 -------- .../postman-task-execution-collection.json | 238 +++++++++++++ .../postman-task-execution.json | 335 ------------------ ...test-workflow.http => task-execution.http} | 63 +++- core.link.json | 2 +- .../task-execution-test-workflow.diagram.json | 4 +- .../extended-tasks-test-workflow.json | 22 +- .../task-execution-test-workflow.json | 12 +- doc/integration-test-documentation.md | 7 +- tests/Core/Helpers/JsonElementAssertions.cs | 45 +++ tests/Core/Helpers/StateFunctionJson.cs | 24 ++ .../TaskExecutionInstanceDataAssertions.cs | 123 +++++++ .../TaskExecutionTestWorkflowTests.cs | 158 +++++++++ 14 files changed, 664 insertions(+), 553 deletions(-) delete mode 100644 api-tests/task-execution/extended-tasks-test-workflow.http delete mode 100644 api-tests/task-execution/postman-extended-tasks.json create mode 100644 api-tests/task-execution/postman-task-execution-collection.json delete mode 100644 api-tests/task-execution/postman-task-execution.json rename api-tests/task-execution/{task-execution-test-workflow.http => task-execution.http} (50%) create mode 100644 tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs create mode 100644 tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs diff --git a/api-tests/task-execution/extended-tasks-test-workflow.http b/api-tests/task-execution/extended-tasks-test-workflow.http deleted file mode 100644 index 02368ae..0000000 --- a/api-tests/task-execution/extended-tasks-test-workflow.http +++ /dev/null @@ -1,40 +0,0 @@ -@baseUrl = http://localhost:4201 -@apiVersion = 1 -@domain = core -@workflow = extended-tasks-test-workflow - -### ============================================================ -### TEST: Dapr task zinciri (sadece dis servis / Dapr) -### Dapr HTTP (1) -> Dapr Service (3) -> Dapr Binding (2) -> -### Dapr PubSub (4) -> completed-state (otomatik) -### ============================================================ - -### Adim 1: Workflow Baslat (sync - tam zincir) -# @name createExtendedInstance -POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true -Content-Type: application/json - -{ - "key": "extended-tasks-test-{{$timestamp}}", - "tags": ["integration-test", "task-execution", "dapr-only"], - "attributes": { - "testId": "extended-tasks-dapr-path" - } -} - -@instanceId = {{createExtendedInstance.response.body.$.id}} - -### Adim 2: Durum Kontrol - completed-state olmali -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state - -### Adim 3: Instance Data - Dapr task sonuclari -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}} - -### ============================================================ -### Beklenen Sonuclar: -### - initCompleted: true -### - taskResults.daprHttp.completed: true -### - taskResults.daprService.completed: true -### - taskResults.daprBinding.completed: true -### - taskResults.daprPubSub.completed: true -### ============================================================ diff --git a/api-tests/task-execution/postman-extended-tasks.json b/api-tests/task-execution/postman-extended-tasks.json deleted file mode 100644 index c8f6bc5..0000000 --- a/api-tests/task-execution/postman-extended-tasks.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "info": { - "_postman_id": "382174e0-fc37-4d36-ba7b-653b40194f3c", - "name": "vNext Test - Extended Tasks (Dapr only)", - "description": "extended-tasks-test-workflow: sadece Dapr HTTP/Service/Binding/PubSub zinciri.\nKlasor: api-tests/task-execution (task-execution grubu altinda).", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "variable": [ - { - "key": "baseUrl", - "value": "http://localhost:4201", - "type": "string" - }, - { - "key": "apiVersion", - "value": "1", - "type": "string" - }, - { - "key": "domain", - "value": "core", - "type": "string" - }, - { - "key": "workflow", - "value": "extended-tasks-test-workflow", - "type": "string" - } - ], - "item": [ - { - "name": "Dapr zinciri: extended-tasks-test-workflow", - "item": [ - { - "name": "Adim 1: Workflow baslat (sync)", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "start" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] - }, - "body": { - "mode": "raw", - "raw": "{\n \"key\": \"extended-tasks-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"task-execution\", \"dapr-only\"],\n \"attributes\": {\n \"testId\": \"extended-tasks-dapr-path\"\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - } - }, - "response": [], - "event": [ - { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "if (jsonData && jsonData.id) {", - " pm.collectionVariables.set(\"instanceId\", jsonData.id);", - " console.log(\"instanceId set to: \" + jsonData.id);", - "}" - ] - } - } - ] - }, - { - "name": "Adim 2: Durum (completed-state)", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceId}}", - "functions", - "state" - ] - } - }, - "response": [] - }, - { - "name": "Adim 3: Instance (Dapr taskResults)", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceId}}" - ] - } - }, - "response": [] - } - ] - } - ] -} diff --git a/api-tests/task-execution/postman-task-execution-collection.json b/api-tests/task-execution/postman-task-execution-collection.json new file mode 100644 index 0000000..cbdd051 --- /dev/null +++ b/api-tests/task-execution/postman-task-execution-collection.json @@ -0,0 +1,238 @@ +{ + "info": { + "_postman_id": "a1b2c3d4-e5f6-4123-a8cd-ef1234567890", + "name": "vNext Test - Task execution (birlesik)", + "description": "task-execution-test-workflow, task-target-workflow, extended-tasks-test-workflow. Sadece happy path. Kaynak: api-tests/task-execution/task-execution.http", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "variable": [ + { "key": "baseUrl", "value": "http://localhost:4201", "type": "string" }, + { "key": "apiVersion", "value": "1", "type": "string" }, + { "key": "domain", "value": "core", "type": "string" }, + { "key": "workflow", "value": "task-execution-test-workflow", "type": "string" }, + { "key": "targetWorkflow", "value": "task-target-workflow", "type": "string" }, + { "key": "extendedWorkflow", "value": "extended-tasks-test-workflow", "type": "string" }, + { "key": "mocklabBaseUrl", "value": "http://localhost:3002", "type": "string" } + ], + "item": [ + { + "name": "1 - Ana zincir (task-execution-test-workflow)", + "item": [ + { + "name": "Adim 1: Ana workflow baslat (sync)", + "request": { + "method": "POST", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"task-exec-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"task-execution\"],\n \"attributes\": {}\n}", + "options": { "raw": { "language": "json" } } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "if (jsonData && jsonData.id) {", + " pm.collectionVariables.set(\"taskExecInstanceId\", jsonData.id);", + "}" + ] + } + } + ] + }, + { + "name": "Adim 2: Durum (human-task-state)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{taskExecInstanceId}}", "functions", "state"] + } + }, + "response": [] + }, + { + "name": "Adim 3: Instance data", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/data", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{taskExecInstanceId}}", "functions", "data"] + } + }, + "response": [] + }, + { + "name": "Adim 4: approve-human-task", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/transitions/approve-human-task", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{taskExecInstanceId}}", "transitions", "approve-human-task"] + }, + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + }, + "response": [] + }, + { + "name": "Adim 5: Durum (completed-state)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{taskExecInstanceId}}", "functions", "state"] + } + }, + "response": [] + }, + { + "name": "Adim 6: Final instance data", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/data", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{taskExecInstanceId}}", "functions", "data"] + } + }, + "response": [] + } + ] + }, + { + "name": "2 - Hedef workflow (task-target-workflow)", + "item": [ + { + "name": "Adim 1: Hedef workflow baslat", + "request": { + "method": "POST", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/start?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{targetWorkflow}}", "instances", "start"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"task-target-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"task-execution\", \"target\"],\n \"attributes\": {}\n}", + "options": { "raw": { "language": "json" } } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "if (jsonData && jsonData.id) {", + " pm.collectionVariables.set(\"targetInstanceId\", jsonData.id);", + "}" + ] + } + } + ] + }, + { + "name": "Adim 2: Hedef workflow durumu", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{targetInstanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{targetWorkflow}}", "instances", "{{targetInstanceId}}", "functions", "state"] + } + }, + "response": [] + } + ] + }, + { + "name": "3 - Extended / Dapr (extended-tasks-test-workflow)", + "item": [ + { + "name": "Adim 1: Workflow baslat (sync)", + "request": { + "method": "POST", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{extendedWorkflow}}/instances/start?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{extendedWorkflow}}", "instances", "start"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"extended-tasks-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"task-execution\", \"dapr-only\"],\n \"attributes\": {\n \"testId\": \"extended-tasks-dapr-path\"\n }\n}", + "options": { "raw": { "language": "json" } } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "if (jsonData && jsonData.id) {", + " pm.collectionVariables.set(\"extendedInstanceId\", jsonData.id);", + "}" + ] + } + } + ] + }, + { + "name": "Adim 2: Durum (completed-state)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{extendedWorkflow}}/instances/{{extendedInstanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{extendedWorkflow}}", "instances", "{{extendedInstanceId}}", "functions", "state"] + } + }, + "response": [] + }, + { + "name": "Adim 3: Instance (Dapr taskResults)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{extendedWorkflow}}/instances/{{extendedInstanceId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{extendedWorkflow}}", "instances", "{{extendedInstanceId}}"] + } + }, + "response": [] + } + ] + } + ] +} diff --git a/api-tests/task-execution/postman-task-execution.json b/api-tests/task-execution/postman-task-execution.json deleted file mode 100644 index b5b2907..0000000 --- a/api-tests/task-execution/postman-task-execution.json +++ /dev/null @@ -1,335 +0,0 @@ -{ - "info": { - "_postman_id": "2835031e-c465-4e76-b09c-a4b3b3e63246", - "name": "vNext Test - Task Execution", - "description": "Auto-generated from task-execution-test-workflow.http.\nAna zincir: task-execution-test-workflow. Dapr-only senaryo: ayri koleksiyon postman-extended-tasks.json (extended-tasks-test-workflow).", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "variable": [ - { - "key": "baseUrl", - "value": "http://localhost:4201", - "type": "string" - }, - { - "key": "apiVersion", - "value": "1", - "type": "string" - }, - { - "key": "domain", - "value": "core", - "type": "string" - }, - { - "key": "workflow", - "value": "task-execution-test-workflow", - "type": "string" - }, - { - "key": "targetWorkflow", - "value": "task-target-workflow", - "type": "string" - }, - { - "key": "mocklabBaseUrl", - "value": "http://localhost:3002", - "type": "string" - } - ], - "item": [ - { - "name": "TEST 1: Full task execution chain", - "item": [ - { - "name": "Adim 1: Ana workflow baslat (sync)", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "start" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] - }, - "body": { - "mode": "raw", - "raw": "{\n \"key\": \"task-exec-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"task-execution\"],\n \"attributes\": {}\n}", - "options": { - "raw": { - "language": "json" - } - } - } - }, - "response": [], - "event": [ - { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "if (jsonData && jsonData.id) {", - " pm.collectionVariables.set(\"taskExecInstanceId\", jsonData.id);", - " console.log(\"taskExecInstanceId set to: \" + jsonData.id);", - "}" - ] - } - } - ] - }, - { - "name": "Adim 2: Durum kontrol (human-task-state beklenir)", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{taskExecInstanceId}}", - "functions", - "state" - ] - } - }, - "response": [] - }, - { - "name": "Adim 3: Instance data (notification / trigger / subprocess / getInstances)", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/data", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{taskExecInstanceId}}", - "functions", - "data" - ] - } - }, - "response": [] - }, - { - "name": "Adim 4: Human task onayla (approve-human-task)", - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/transitions/approve-human-task", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{taskExecInstanceId}}", - "transitions", - "approve-human-task" - ] - }, - "body": { - "mode": "raw", - "raw": "{}", - "options": { - "raw": { - "language": "json" - } - } - } - }, - "response": [] - }, - { - "name": "Adim 5: Durum kontrol (completed-state)", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{taskExecInstanceId}}", - "functions", - "state" - ] - } - }, - "response": [] - }, - { - "name": "Adim 6: Final instance data", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/data", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{taskExecInstanceId}}", - "functions", - "data" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "TEST 2: Hedef workflow (task-target-workflow) bagimsiz baslatma", - "item": [ - { - "name": "Adim 1: Hedef workflow baslat", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/start?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{targetWorkflow}}", - "instances", - "start" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] - }, - "body": { - "mode": "raw", - "raw": "{\n \"key\": \"task-target-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"task-execution\", \"target\"],\n \"attributes\": {}\n}", - "options": { - "raw": { - "language": "json" - } - } - } - }, - "response": [], - "event": [ - { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "if (jsonData && jsonData.id) {", - " pm.collectionVariables.set(\"targetInstanceId\", jsonData.id);", - " console.log(\"targetInstanceId set to: \" + jsonData.id);", - "}" - ] - } - } - ] - }, - { - "name": "Adim 2: Hedef workflow durumu (target-completed olmali)", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{targetInstanceId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{targetWorkflow}}", - "instances", - "{{targetInstanceId}}", - "functions", - "state" - ] - } - }, - "response": [] - } - ] - } - ] -} \ No newline at end of file diff --git a/api-tests/task-execution/task-execution-test-workflow.http b/api-tests/task-execution/task-execution.http similarity index 50% rename from api-tests/task-execution/task-execution-test-workflow.http rename to api-tests/task-execution/task-execution.http index da86b16..b7a01c4 100644 --- a/api-tests/task-execution/task-execution-test-workflow.http +++ b/api-tests/task-execution/task-execution.http @@ -3,17 +3,26 @@ @domain = core @workflow = task-execution-test-workflow @targetWorkflow = task-target-workflow +@extendedWorkflow = extended-tasks-test-workflow @mocklabBaseUrl = http://localhost:3002 ############################################################################### -# TEST 1: Full task execution chain +# vNext Task execution — tek dosya (Postman koleksiyonu ile ayni bolum yapisi) +# +# BOLUM 1 — Ana zincir (task-execution-test-workflow): happy path +# BOLUM 2 — Hedef workflow (task-target-workflow): happy path +# BOLUM 3 — Dapr zinciri (extended-tasks-test-workflow): happy path +############################################################################### + +############################################################################### +# BOLUM 1 — Happy path: Ana task execution zinciri # HTTP -> Script -> Cross workflow -> Start Flow (11) -> Get Instance Data (13) # -> Notification (10) -> TriggerTransition (12) -> SubProcess (14) # -> GetInstances (15) -> Human Task (5) -> PATCH approve -> Completed # Requires: MocklabBaseUrl config, Mocklab seeded (POST /api/test/process) ############################################################################### -### Adim 1: Ana workflow baslat (sync zincir human-task-state'te durur) +### B1 — Adim 1: Ana workflow baslat (sync zincir human-task-state'te durur) # @name createTaskExecInstance POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json @@ -26,10 +35,10 @@ Content-Type: application/json @taskExecInstanceId = {{createTaskExecInstance.response.body.$.id}} -### Adim 2: Durum kontrol (human-task-state beklenir) +### B1 — Adim 2: Durum kontrol (human-task-state beklenir) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/state -### Adim 3: Instance data (notification / trigger / subprocess / getInstances bayraklari) +### B1 — Adim 3: Instance data (notification / trigger / subprocess / getInstances bayraklari) # Beklenen: testId, httpTaskCompleted, scriptProcessed, crossWorkflowCompleted, # startFlowCompleted, startedInstanceId, getInstanceDataCompleted, # taskResults.notification.completed, taskResults.triggerTransition.completed, @@ -37,23 +46,23 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/ # taskResults.humanTask.completed GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/data -### Adim 4: Human task onayla (manuel transition) +### B1 — Adim 4: Human task onayla (manuel transition) PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/transitions/approve-human-task Content-Type: application/json {} -### Adim 5: Durum kontrol (completed-state) +### B1 — Adim 5: Durum kontrol (completed-state) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/state -### Adim 6: Final instance data +### B1 — Adim 6: Final instance data GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/data ############################################################################### -# TEST 2: Hedef workflow (task-target-workflow) bagimsiz baslatma +# BOLUM 2 — Happy path: Hedef workflow (task-target-workflow) bagimsiz baslatma ############################################################################### -### Adim 1: Hedef workflow baslat +### B2 — Adim 1: Hedef workflow baslat # @name createTargetInstance POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/start?sync=true Content-Type: application/json @@ -66,5 +75,39 @@ Content-Type: application/json @targetInstanceId = {{createTargetInstance.response.body.$.id}} -### Adim 2: Hedef workflow durumu (target-completed olmali) +### B2 — Adim 2: Hedef workflow durumu (target-completed olmali) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{targetInstanceId}}/functions/state + +############################################################################### +# BOLUM 3 — Happy path: Dapr task zinciri (extended-tasks-test-workflow, sadece dis servis) +# Dapr HTTP (1) -> Dapr Service (3) -> Dapr Binding (2) -> +# Dapr PubSub (4) -> completed-state (otomatik) +############################################################################### + +### B3 — Adim 1: Workflow baslat (sync - tam zincir) +# @name createExtendedInstance +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{extendedWorkflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "extended-tasks-test-{{$timestamp}}", + "tags": ["integration-test", "task-execution", "dapr-only"], + "attributes": { + "testId": "extended-tasks-dapr-path" + } +} + +@extendedInstanceId = {{createExtendedInstance.response.body.$.id}} + +### B3 — Adim 2: Durum kontrol - completed-state olmali +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{extendedWorkflow}}/instances/{{extendedInstanceId}}/functions/state + +### B3 — Adim 3: Instance - Dapr task sonuclari +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{extendedWorkflow}}/instances/{{extendedInstanceId}} + +### B3 — Beklenen sonuclar (yorum): +### - initCompleted: true +### - taskResults.daprHttp.completed: true +### - taskResults.daprService.completed: true +### - taskResults.daprBinding.completed: true +### - taskResults.daprPubSub.completed: true diff --git a/core.link.json b/core.link.json index d0dc643..6055407 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-04-30T09:51:04.502Z" + "importedAt": "2026-04-30T11:55:47.337Z" } \ No newline at end of file diff --git a/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json b/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json index d7bfa73..206dc46 100644 --- a/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json +++ b/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json @@ -33,8 +33,8 @@ "y": -10 }, "get-instances-state": { - "x": 2600, - "y": 380 + "x": 2680, + "y": 360 }, "subprocess-state": { "x": 2460, diff --git a/core/Workflows/task-execution/extended-tasks-test-workflow.json b/core/Workflows/task-execution/extended-tasks-test-workflow.json index 99f20d8..1bf4ae5 100644 --- a/core/Workflows/task-execution/extended-tasks-test-workflow.json +++ b/core/Workflows/task-execution/extended-tasks-test-workflow.json @@ -75,7 +75,7 @@ }, "mapping": { "location": "./src/extended-tasks-test-workflow/InitExtendedTaskMapping.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEluaXRFeHRlbmRlZFRhc2tNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpCiAgICAgICAgICAgIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKCiAgICAgICAgcmVzdWx0LmluaXRDb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC5pbml0QXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiSW5pdEV4dGVuZGVkVGFza01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } ], @@ -98,7 +98,7 @@ ], "rule": { "location": "./src/shared/AlwaysTrueRule.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" }, "onExecutionTasks": [] } @@ -132,7 +132,7 @@ }, "mapping": { "location": "./src/extended-tasks-test-workflow/DaprHttpMapping.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIERhcHJIdHRwTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpbml0Q29tcGxldGVkIikpIHJlc3VsdC5pbml0Q29tcGxldGVkID0gZGF0YS5pbml0Q29tcGxldGVkOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwclNlcnZpY2UiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJTZXJ2aWNlID0gZGF0YS50YXNrUmVzdWx0cy5kYXByU2VydmljZTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInRyaWdnZXJUcmFuc2l0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJzdWJwcm9jZXNzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzID0gZGF0YS50YXNrUmVzdWx0cy5zdWJwcm9jZXNzOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJIdHRwID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwckh0dHAuY29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwckh0dHAuZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICB2YXIgdGFza1Jlc3BvbnNlID0gY29udGV4dC5Cb2R5OwogICAgICAgIGlmICh0YXNrUmVzcG9uc2UgIT0gbnVsbCAmJiBIYXNQcm9wZXJ0eSh0YXNrUmVzcG9uc2UsICJkYXRhIikpCiAgICAgICAgewogICAgICAgICAgICB2YXIgcmVzcG9uc2VEYXRhID0gdGFza1Jlc3BvbnNlLmRhdGE7CiAgICAgICAgICAgIGlmIChyZXNwb25zZURhdGEgIT0gbnVsbCAmJiBIYXNQcm9wZXJ0eShyZXNwb25zZURhdGEsICJwcm9jZXNzSWQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXBySHR0cC5wcm9jZXNzSWQgPSByZXNwb25zZURhdGEucHJvY2Vzc0lkOwogICAgICAgIH0KCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkRhcHJIdHRwTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } ], @@ -155,7 +155,7 @@ ], "rule": { "location": "./src/shared/AlwaysTrueRule.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" }, "onExecutionTasks": [] } @@ -189,7 +189,7 @@ }, "mapping": { "location": "./src/extended-tasks-test-workflow/DaprServiceMapping.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIERhcHJTZXJ2aWNlTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpbml0Q29tcGxldGVkIikpIHJlc3VsdC5pbml0Q29tcGxldGVkID0gZGF0YS5pbml0Q29tcGxldGVkOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwckh0dHAiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJIdHRwID0gZGF0YS50YXNrUmVzdWx0cy5kYXBySHR0cDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInRyaWdnZXJUcmFuc2l0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJzdWJwcm9jZXNzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzID0gZGF0YS50YXNrUmVzdWx0cy5zdWJwcm9jZXNzOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJTZXJ2aWNlID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwclNlcnZpY2UuY29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwclNlcnZpY2UuZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICB2YXIgdGFza1Jlc3BvbnNlID0gY29udGV4dC5Cb2R5OwogICAgICAgIGlmICh0YXNrUmVzcG9uc2UgIT0gbnVsbCAmJiBIYXNQcm9wZXJ0eSh0YXNrUmVzcG9uc2UsICJkYXRhIikpCiAgICAgICAgewogICAgICAgICAgICB2YXIgcmVzcG9uc2VEYXRhID0gdGFza1Jlc3BvbnNlLmRhdGE7CiAgICAgICAgICAgIGlmIChyZXNwb25zZURhdGEgIT0gbnVsbCAmJiBIYXNQcm9wZXJ0eShyZXNwb25zZURhdGEsICJwcm9jZXNzSWQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByU2VydmljZS5wcm9jZXNzSWQgPSByZXNwb25zZURhdGEucHJvY2Vzc0lkOwogICAgICAgIH0KCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkRhcHJTZXJ2aWNlTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } ], @@ -212,7 +212,7 @@ ], "rule": { "location": "./src/shared/AlwaysTrueRule.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" }, "onExecutionTasks": [] } @@ -246,7 +246,7 @@ }, "mapping": { "location": "./src/extended-tasks-test-workflow/DaprBindingMapping.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIERhcHJCaW5kaW5nTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpbml0Q29tcGxldGVkIikpIHJlc3VsdC5pbml0Q29tcGxldGVkID0gZGF0YS5pbml0Q29tcGxldGVkOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwckh0dHAiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJIdHRwID0gZGF0YS50YXNrUmVzdWx0cy5kYXBySHR0cDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXByU2VydmljZSIpKSByZXN1bHQudGFza1Jlc3VsdHMuZGFwclNlcnZpY2UgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJTZXJ2aWNlOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgIm5vdGlmaWNhdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uID0gZGF0YS50YXNrUmVzdWx0cy5ub3RpZmljYXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAidHJpZ2dlclRyYW5zaXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uID0gZGF0YS50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJnZXRJbnN0YW5jZXMiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IGRhdGEudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgfQoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwckJpbmRpbmcgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByQmluZGluZy5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByQmluZGluZy5leGVjdXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCJEYXByQmluZGluZ01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } ], @@ -269,7 +269,7 @@ ], "rule": { "location": "./src/shared/AlwaysTrueRule.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" }, "onExecutionTasks": [] } @@ -303,7 +303,7 @@ }, "mapping": { "location": "./src/extended-tasks-test-workflow/DaprPubSubMapping.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIERhcHJQdWJTdWJNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImluaXRDb21wbGV0ZWQiKSkgcmVzdWx0LmluaXRDb21wbGV0ZWQgPSBkYXRhLmluaXRDb21wbGV0ZWQ7CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0YXNrUmVzdWx0cyIpKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXBySHR0cCIpKSByZXN1bHQudGFza1Jlc3VsdHMuZGFwckh0dHAgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJIdHRwOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImRhcHJTZXJ2aWNlIikpIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByU2VydmljZSA9IGRhdGEudGFza1Jlc3VsdHMuZGFwclNlcnZpY2U7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwckJpbmRpbmciKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJCaW5kaW5nID0gZGF0YS50YXNrUmVzdWx0cy5kYXByQmluZGluZzsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInRyaWdnZXJUcmFuc2l0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJzdWJwcm9jZXNzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzID0gZGF0YS50YXNrUmVzdWx0cy5zdWJwcm9jZXNzOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByUHViU3ViLmNvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIuZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiRGFwclB1YlN1Yk1hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } ], @@ -326,7 +326,7 @@ ], "rule": { "location": "./src/shared/AlwaysTrueRule.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" }, "onExecutionTasks": [] } @@ -355,4 +355,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/task-execution/task-execution-test-workflow.json b/core/Workflows/task-execution/task-execution-test-workflow.json index e3dce52..a6fba87 100644 --- a/core/Workflows/task-execution/task-execution-test-workflow.json +++ b/core/Workflows/task-execution/task-execution-test-workflow.json @@ -381,7 +381,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/NotificationMapping.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIE5vdGlmaWNhdGlvbk1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyb3NzV29ya2Zsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQgPSBkYXRhLmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcm9zc1dvcmtmbG93QXQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dBdCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0F0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJodW1hblRhc2siKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IGRhdGEudGFza1Jlc3VsdHMuaHVtYW5UYXNrOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbi5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24uZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiTm90aWZpY2F0aW9uTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } ], @@ -438,7 +438,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/TriggerTransitionMapping.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFRyaWdnZXJUcmFuc2l0aW9uTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkQXQiKSkgcmVzdWx0LnN0YXJ0ZWRBdCA9IGRhdGEuc3RhcnRlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBTdGF0dXNDb2RlIikpIHJlc3VsdC5odHRwU3RhdHVzQ29kZSA9IGRhdGEuaHR0cFN0YXR1c0NvZGU7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwSXNTdWNjZXNzIikpIHJlc3VsdC5odHRwSXNTdWNjZXNzID0gZGF0YS5odHRwSXNTdWNjZXNzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicHJvY2Vzc0lkIikpIHJlc3VsdC5wcm9jZXNzSWQgPSBkYXRhLnByb2Nlc3NJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInNjcmlwdFByb2Nlc3NlZCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkID0gZGF0YS5zY3JpcHRQcm9jZXNzZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWRBdCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkQXQgPSBkYXRhLnNjcmlwdFByb2Nlc3NlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCIpKSByZXN1bHQuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyb3NzV29ya2Zsb3dBdCIpKSByZXN1bHQuY3Jvc3NXb3JrZmxvd0F0ID0gZGF0YS5jcm9zc1dvcmtmbG93QXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydEZsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LnN0YXJ0Rmxvd0NvbXBsZXRlZCA9IGRhdGEuc3RhcnRGbG93Q29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93SXNTdWNjZXNzIikpIHJlc3VsdC5zdGFydEZsb3dJc1N1Y2Nlc3MgPSBkYXRhLnN0YXJ0Rmxvd0lzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRJbnN0YW5jZUlkIikpIHJlc3VsdC5zdGFydGVkSW5zdGFuY2VJZCA9IGRhdGEuc3RhcnRlZEluc3RhbmNlSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJnZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQiKSkgcmVzdWx0LmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZCA9IGRhdGEuZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3MgPSBkYXRhLmdldEluc3RhbmNlRGF0YUlzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInJlbW90ZUluc3RhbmNlRGF0YSIpKSByZXN1bHQucmVtb3RlSW5zdGFuY2VEYXRhID0gZGF0YS5yZW1vdGVJbnN0YW5jZURhdGE7CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0YXNrUmVzdWx0cyIpKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInRyaWdnZXJUcmFuc2l0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAic3VicHJvY2VzcyIpKSByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2VzcyA9IGRhdGEudGFza1Jlc3VsdHMuc3VicHJvY2VzczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJnZXRJbnN0YW5jZXMiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IGRhdGEudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImh1bWFuVGFzayIpKSByZXN1bHQudGFza1Jlc3VsdHMuaHVtYW5UYXNrID0gZGF0YS50YXNrUmVzdWx0cy5odW1hblRhc2s7CiAgICAgICAgfQoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbi5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbi5leGVjdXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCJUcmlnZ2VyVHJhbnNpdGlvbk1hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } ], @@ -495,7 +495,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/SubProcessMapping.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFN1YlByb2Nlc3NNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRBdCIpKSByZXN1bHQuc3RhcnRlZEF0ID0gZGF0YS5zdGFydGVkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwVGFza0NvbXBsZXRlZCIpKSByZXN1bHQuaHR0cFRhc2tDb21wbGV0ZWQgPSBkYXRhLmh0dHBUYXNrQ29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFN0YXR1c0NvZGUiKSkgcmVzdWx0Lmh0dHBTdGF0dXNDb2RlID0gZGF0YS5odHRwU3RhdHVzQ29kZTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBJc1N1Y2Nlc3MiKSkgcmVzdWx0Lmh0dHBJc1N1Y2Nlc3MgPSBkYXRhLmh0dHBJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJwcm9jZXNzSWQiKSkgcmVzdWx0LnByb2Nlc3NJZCA9IGRhdGEucHJvY2Vzc0lkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkIikpIHJlc3VsdC5zY3JpcHRQcm9jZXNzZWQgPSBkYXRhLnNjcmlwdFByb2Nlc3NlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInNjcmlwdFByb2Nlc3NlZEF0IikpIHJlc3VsdC5zY3JpcHRQcm9jZXNzZWRBdCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcm9zc1dvcmtmbG93Q29tcGxldGVkIikpIHJlc3VsdC5jcm9zc1dvcmtmbG93Q29tcGxldGVkID0gZGF0YS5jcm9zc1dvcmtmbG93Q29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3Jvc3NXb3JrZmxvd0F0IikpIHJlc3VsdC5jcm9zc1dvcmtmbG93QXQgPSBkYXRhLmNyb3NzV29ya2Zsb3dBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0NvbXBsZXRlZCIpKSByZXN1bHQuc3RhcnRGbG93Q29tcGxldGVkID0gZGF0YS5zdGFydEZsb3dDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydEZsb3dJc1N1Y2Nlc3MiKSkgcmVzdWx0LnN0YXJ0Rmxvd0lzU3VjY2VzcyA9IGRhdGEuc3RhcnRGbG93SXNTdWNjZXNzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEluc3RhbmNlSWQiKSkgcmVzdWx0LnN0YXJ0ZWRJbnN0YW5jZUlkID0gZGF0YS5zdGFydGVkSW5zdGFuY2VJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUNvbXBsZXRlZCIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkID0gZGF0YS5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJnZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3MiKSkgcmVzdWx0LmdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyA9IGRhdGEuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicmVtb3RlSW5zdGFuY2VEYXRhIikpIHJlc3VsdC5yZW1vdGVJbnN0YW5jZURhdGEgPSBkYXRhLnJlbW90ZUluc3RhbmNlRGF0YTsKCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRhc2tSZXN1bHRzIikpCiAgICAgICAgewogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgIm5vdGlmaWNhdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uID0gZGF0YS50YXNrUmVzdWx0cy5ub3RpZmljYXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAidHJpZ2dlclRyYW5zaXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uID0gZGF0YS50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJzdWJwcm9jZXNzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzID0gZGF0YS50YXNrUmVzdWx0cy5zdWJwcm9jZXNzOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImdldEluc3RhbmNlcyIpKSByZXN1bHQudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzID0gZGF0YS50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXM7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiaHVtYW5UYXNrIikpIHJlc3VsdC50YXNrUmVzdWx0cy5odW1hblRhc2sgPSBkYXRhLnRhc2tSZXN1bHRzLmh1bWFuVGFzazsKICAgICAgICB9CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2Vzcy5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzLmV4ZWN1dGVkQXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlN1YlByb2Nlc3NNYXBwaW5nIGNvbXBsZXRlZCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" } } ], @@ -552,7 +552,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/GetInstancesMapping.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEdldEluc3RhbmNlc01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyb3NzV29ya2Zsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQgPSBkYXRhLmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcm9zc1dvcmtmbG93QXQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dBdCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0F0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJodW1hblRhc2siKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IGRhdGEudGFza1Jlc3VsdHMuaHVtYW5UYXNrOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcy5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMuZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICB2YXIgdGFza1Jlc3BvbnNlID0gY29udGV4dC5Cb2R5OwogICAgICAgIGlmICh0YXNrUmVzcG9uc2UgIT0gbnVsbCAmJiBIYXNQcm9wZXJ0eSh0YXNrUmVzcG9uc2UsICJkYXRhIikpCiAgICAgICAgewogICAgICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzLnJlc3BvbnNlUmVjZWl2ZWQgPSB0cnVlOwogICAgICAgIH0KCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkdldEluc3RhbmNlc01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } ], @@ -609,7 +609,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/HumanTaskMapping.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEh1bWFuVGFza01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyb3NzV29ya2Zsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQgPSBkYXRhLmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcm9zc1dvcmtmbG93QXQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dBdCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0F0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJodW1hblRhc2siKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IGRhdGEudGFza1Jlc3VsdHMuaHVtYW5UYXNrOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzay5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5odW1hblRhc2suYXBwcm92ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiSHVtYW5UYXNrTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } ], @@ -657,4 +657,4 @@ } ] } -} \ No newline at end of file +} diff --git a/doc/integration-test-documentation.md b/doc/integration-test-documentation.md index 19db4f4..7372fe1 100644 --- a/doc/integration-test-documentation.md +++ b/doc/integration-test-documentation.md @@ -325,7 +325,7 @@ subflow-orchestration-parent (F) **Workflow'lar:** - `task-execution-test-workflow` (type: F) — Ana test workflow'u (HTTP, Script, StartFlow, GetInstanceData, Notification, TriggerTransition, SubProcess, GetInstances, Human task + manuel onay) - `task-target-workflow` (type: F) — Cross-workflow / StartFlow / GetInstanceData / TriggerTransition / SubProcess / GetInstances icin hedef -- `extended-tasks-test-workflow` (type: F) — **Sadece Dapr** ile dis servis cagrisi: HTTP invoke, Service, Binding, PubSub (tek zincir, `api-tests/task-execution/extended-tasks-test-workflow.http`) +- `extended-tasks-test-workflow` (type: F) — **Sadece Dapr** ile dis servis cagrisi: HTTP invoke, Service, Binding, PubSub (tek zincir; HTTP test: `api-tests/task-execution/task-execution.http` **Bolum 3**) **Amac:** Tum **runtime-ici** gorev tipleri ile **Dapr tabanli** gorev tiplerini ayri workflow'larda dogrular; task siralama (`onEntries` order), `context.Body.data`, cross-workflow iletisim ve Dapr bilesen YAML (`etc/dapr/components/`) ile uyumu kapsar. @@ -450,10 +450,9 @@ extended-tasks-test-workflow (F) | CSX | `extended-tasks-test-workflow` mapping'leri | `Workflows/task-execution/src/extended-tasks-test-workflow/*.csx` | | CSX | Ortak auto-transition rule | `Workflows/task-execution/src/shared/AlwaysTrueRule.csx` | -**HTTP / Postman:** `api-tests/task-execution/task-execution-test-workflow.http` (ana zincir + human onay), `api-tests/task-execution/extended-tasks-test-workflow.http` (Dapr-only). Mini Postman: `postman-task-execution.json`, `postman-extended-tasks.json` (`api-tests/task-execution/` klasorunde). +**HTTP / Postman:** `api-tests/task-execution/task-execution.http` (Bolum 1-3, happy path). Postman: `postman-task-execution-collection.json` (aynı üç bölüm). - -## Grup 4: Error Boundary +**C# integration testler:** `tests/Core/Tests/task-execution-test-workflow/` (`TaskExecutionTestWorkflowTests`, `TaskExecutionInstanceDataAssertions`) — B1/B2/B3 happy path, ana workflow için instance data sözleşmesi ve extended Dapr `taskResults` bayrakları. **Workflow:** `error-boundary-test-workflow` (type: F) diff --git a/tests/Core/Helpers/JsonElementAssertions.cs b/tests/Core/Helpers/JsonElementAssertions.cs index 5cc9cf0..fc3be71 100644 --- a/tests/Core/Helpers/JsonElementAssertions.cs +++ b/tests/Core/Helpers/JsonElementAssertions.cs @@ -46,4 +46,49 @@ public static void AssertPropertyNonEmptyString( message ?? $"Property '{name}' should be a non-empty string." ); } + + /// + /// Follows as nested objects and returns the final segment object (e.g. ["taskResults","notification"]). + /// + public static bool TryGetNestedObject( + JsonElement root, + string[] path, + out JsonElement nested + ) + { + nested = default; + var current = root; + foreach (var segment in path) + { + if ( + current.ValueKind != JsonValueKind.Object + || !current.TryGetProperty(segment, out var next) + ) + return false; + current = next; + } + + nested = current; + return true; + } + + public static void AssertNestedPropertyTrue( + JsonElement root, + string[] pathToParent, + string booleanPropertyName, + string? message = null + ) + { + Assert.True( + TryGetNestedObject(root, pathToParent, out var parent) + && parent.ValueKind == JsonValueKind.Object, + message + ?? $"Expected object at path '{string.Join(".", pathToParent)}'." + ); + AssertPropertyTrue( + parent, + booleanPropertyName, + message ?? $"'{booleanPropertyName}' should be true under {string.Join(".", pathToParent)}." + ); + } } diff --git a/tests/Core/Helpers/StateFunctionJson.cs b/tests/Core/Helpers/StateFunctionJson.cs index 1a1f8a5..8e56652 100644 --- a/tests/Core/Helpers/StateFunctionJson.cs +++ b/tests/Core/Helpers/StateFunctionJson.cs @@ -32,6 +32,30 @@ public static string ExtractStateName(JsonElement body) return string.Empty; } + /// + /// Runtime instance status from state function body (e.g. A/B/C/F); shape may vary by version. + /// + public static string? ExtractStatus(JsonElement body) + { + if ( + body.ValueKind == JsonValueKind.Object + && body.TryGetProperty("status", out var direct) + && direct.ValueKind == JsonValueKind.String + ) + return direct.GetString(); + + if ( + body.ValueKind == JsonValueKind.Object + && body.TryGetProperty("data", out var data) + && data.ValueKind == JsonValueKind.Object + && data.TryGetProperty("status", out var nested) + && nested.ValueKind == JsonValueKind.String + ) + return nested.GetString(); + + return null; + } + /// /// Whether .transitions[] contains an item whose name equals (primary for runtime state function), or whose key equals it if present. /// diff --git a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs new file mode 100644 index 0000000..a02e287 --- /dev/null +++ b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs @@ -0,0 +1,123 @@ +using System.Text.Json; +using Core.IntegrationTests.Helpers; +using Xunit; + +namespace Core.IntegrationTests.Tests.TaskExecutionTestWorkflow; + +/// +/// Instance attributes sözleşmesi: api-tests/task-execution/task-execution.http B1/B3 yorumları ile +/// core/Workflows/task-execution/src/.../*.csx hizalıdır. +/// +internal static class TaskExecutionMainWorkflowInstanceDataAssertions +{ + private const string ContractHint = + "(task-execution-test-workflow mappings + task-execution.http B1 expected flags)"; + + /// + /// Human-task beklemeden önce: HTTP, script, cross-workflow, start-flow, get-instance-data, + /// notification, trigger, subprocess, get-instances ve human-task onEntry mapping bayrakları. + /// + public static void AssertWhileWaitingOnHumanTask(JsonElement attributes) + { + JsonElementAssertions.AssertPropertyNonEmptyString( + attributes, + "testId", + $"testId {ContractHint}" + ); + JsonElementAssertions.AssertPropertyTrue( + attributes, + "httpTaskCompleted", + ContractHint + ); + JsonElementAssertions.AssertPropertyTrue(attributes, "scriptProcessed", ContractHint); + JsonElementAssertions.AssertPropertyTrue( + attributes, + "crossWorkflowCompleted", + ContractHint + ); + JsonElementAssertions.AssertPropertyTrue( + attributes, + "startFlowCompleted", + ContractHint + ); + JsonElementAssertions.AssertPropertyNonEmptyString( + attributes, + "startedInstanceId", + ContractHint + ); + JsonElementAssertions.AssertPropertyTrue( + attributes, + "getInstanceDataCompleted", + ContractHint + ); + + JsonElementAssertions.AssertNestedPropertyTrue( + attributes, + new[] { "taskResults", "notification" }, + "completed", + ContractHint + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attributes, + new[] { "taskResults", "triggerTransition" }, + "completed", + ContractHint + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attributes, + new[] { "taskResults", "subprocess" }, + "completed", + ContractHint + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attributes, + new[] { "taskResults", "getInstances" }, + "completed", + ContractHint + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attributes, + new[] { "taskResults", "humanTask" }, + "completed", + ContractHint + ); + } +} + +/// +/// extended-tasks-test-workflow sonunda Dapr zinciri için attributes kontrolleri. +/// +internal static class ExtendedTasksWorkflowInstanceDataAssertions +{ + private const string ContractHint = + "(extended-tasks-test-workflow Dapr mappings + task-execution.http B3 yorumları)"; + + public static void AssertDaprChainCompleted(JsonElement attributes) + { + JsonElementAssertions.AssertPropertyTrue(attributes, "initCompleted", ContractHint); + JsonElementAssertions.AssertNestedPropertyTrue( + attributes, + new[] { "taskResults", "daprHttp" }, + "completed", + ContractHint + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attributes, + new[] { "taskResults", "daprService" }, + "completed", + ContractHint + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attributes, + new[] { "taskResults", "daprBinding" }, + "completed", + ContractHint + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attributes, + new[] { "taskResults", "daprPubSub" }, + "completed", + ContractHint + ); + } +} diff --git a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs new file mode 100644 index 0000000..d2029d5 --- /dev/null +++ b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs @@ -0,0 +1,158 @@ +using System.Text.Json; +using Core.IntegrationTests.Helpers; +using Core.IntegrationTests.Infrastructure; +using Xunit; + +namespace Core.IntegrationTests.Tests.TaskExecutionTestWorkflow; + +/// +/// api-tests/task-execution/task-execution.http B1–B3 ve +/// doc/integration-test-documentation.md Task Execution grubu ile hizalı integration testler. +/// +public class TaskExecutionTestWorkflowTests : IntegrationTestBase +{ + private const string MainWorkflowKey = "task-execution-test-workflow"; + private const string TargetWorkflowKey = "task-target-workflow"; + private const string ExtendedWorkflowKey = "extended-tasks-test-workflow"; + + /// Ana zincir: Mocklab HTTP, subprocess, harici servisler — toleranslı bekleme. + private static readonly TimeSpan MainWorkflowStateTimeout = TimeSpan.FromMinutes(3); + + private static readonly TimeSpan ExtendedDaprTimeout = TimeSpan.FromMinutes(3); + + private readonly WorkflowInstanceTestHelper _mainWorkflow; + private readonly WorkflowInstanceTestHelper _targetWorkflow; + private readonly WorkflowInstanceTestHelper _extendedWorkflow; + + public TaskExecutionTestWorkflowTests(VNextTestEnvironment environment) + : base(environment) + { + _mainWorkflow = new WorkflowInstanceTestHelper(Api, MainWorkflowKey); + _targetWorkflow = new WorkflowInstanceTestHelper(Api, TargetWorkflowKey); + _extendedWorkflow = new WorkflowInstanceTestHelper(Api, ExtendedWorkflowKey); + } + + [Fact] + public async Task B1_MainWorkflow_HappyPath_InstanceDataThenApprove_ReachesCompleted() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("task-exec-it"); + var instanceId = await _mainWorkflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "integration-test", "task-execution" }, + attributes = new { }, + } + ); + + await _mainWorkflow.WaitForStateAsync( + instanceId, + "human-task-state", + MainWorkflowStateTimeout + ); + + var stateBody = await _mainWorkflow.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.True( + StateFunctionJson.TransitionsContainKey(stateBody, "approve-human-task"), + "human-task-state should expose approve-human-task transition" + ); + + var attrsBefore = await GetAttributesAsync(MainWorkflowKey, instanceId); + TaskExecutionMainWorkflowInstanceDataAssertions.AssertWhileWaitingOnHumanTask(attrsBefore); + + await _mainWorkflow.RunTransitionAsync( + instanceId, + "approve-human-task", + headers: null, + transitionBody: new { } + ); + + await _mainWorkflow.AssertStateAsync(instanceId, "completed-state"); + + var stateCompleted = await _mainWorkflow.GetStateFunctionBodyAsync( + instanceId, + headers: null + ); + var status = StateFunctionJson.ExtractStatus(stateCompleted); + // Happy path bittiğinde instance tamamlanmıştır; GET .../functions/state gövdesindeki status 'C' (Completed) olmalıdır. + Assert.Equal("C", status); + + var attrsAfter = await GetAttributesAsync(MainWorkflowKey, instanceId); + TaskExecutionMainWorkflowInstanceDataAssertions.AssertWhileWaitingOnHumanTask(attrsAfter); + } + + [Fact] + public async Task B2_TargetWorkflow_HappyPath_ReachesTargetCompleted() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("task-target-it"); + var instanceId = await _targetWorkflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "integration-test", "task-execution", "target" }, + attributes = new { }, + } + ); + + await _targetWorkflow.WaitForStateAsync( + instanceId, + "target-completed", + TimeSpan.FromSeconds(30) + ); + + var targetStateBody = await _targetWorkflow.GetStateFunctionBodyAsync( + instanceId, + headers: null + ); + var targetStatus = StateFunctionJson.ExtractStatus(targetStateBody); + // Hedef workflow happy path sonunda state fonksiyonu status 'C' (Completed) olmalıdır. + Assert.Equal("C", targetStatus); + } + + [Fact] + public async Task B3_ExtendedWorkflow_DaprChain_ReachesCompleted_WithExpectedTaskResults() + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey("extended-dapr-it"); + var instanceId = await _extendedWorkflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "integration-test", "task-execution", "dapr-only" }, + attributes = new { testId = "extended-tasks-dapr-path" }, + } + ); + + await _extendedWorkflow.WaitForStateAsync( + instanceId, + "completed-state", + ExtendedDaprTimeout + ); + + var extendedStateBody = await _extendedWorkflow.GetStateFunctionBodyAsync( + instanceId, + headers: null + ); + var extendedStatus = StateFunctionJson.ExtractStatus(extendedStateBody); + // Dapr zinciri happy path sonunda state fonksiyonu status 'C' (Completed) olmalıdır. + Assert.Equal("C", extendedStatus); + + var attrs = await GetAttributesAsync(ExtendedWorkflowKey, instanceId); + JsonElementAssertions.AssertPropertyString( + attrs, + "testId", + "extended-tasks-dapr-path", + "start body testId should be preserved through init mapping" + ); + ExtendedTasksWorkflowInstanceDataAssertions.AssertDaprChainCompleted(attrs); + } + + private async Task GetAttributesAsync(string workflowKey, string instanceId) + { + var response = await Api.GetInstanceAsync(workflowKey, instanceId); + Assert.True( + response.Body.TryGetProperty("attributes", out var attributes), + "GetInstance response should include attributes" + ); + return attributes; + } +} From 785f27f19c87cb72591e6dc13a2b958113e0a573 Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Thu, 30 Apr 2026 15:37:01 +0300 Subject: [PATCH 07/24] =?UTF-8?q?Previously=20we=20only=20asserted=20tasks?= =?UTF-8?q?=20indirectly=20through=20instance=20data,=20and=20scripts=20we?= =?UTF-8?q?re=20the=20ones=20putting=20values=20there.=20That=20produced?= =?UTF-8?q?=20a=20lot=20of=20false=20positives=E2=80=94we=20were=20mainly?= =?UTF-8?q?=20checking=20that=20scripts=20ran,=20not=20that=20the=20tasks?= =?UTF-8?q?=20themselves=20behaved=20correctly.=20Now=20many=20tasks=20are?= =?UTF-8?q?=20actually=20tested.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../postman-task-execution-collection.json | 16 +++++- api-tests/task-execution/task-execution.http | 12 +++-- core.link.json | 2 +- .../trigger-transition-task.json | 2 +- .../.meta/task-target-workflow.diagram.json | 16 ++++++ .../GetInstanceDataMapping.csx | 6 +-- .../NotificationMapping.csx | 53 ------------------- .../StartFlowMapping.csx | 9 +++- .../TriggerTransitionMapping.csx | 11 ++++ .../task-execution-test-workflow.json | 9 ++-- .../task-execution/task-target-workflow.json | 12 ++--- doc/integration-test-documentation.md | 8 +-- .../TaskExecutionInstanceDataAssertions.cs | 10 ++-- .../TaskExecutionTestWorkflowTests.cs | 32 +++++++++++ 14 files changed, 110 insertions(+), 88 deletions(-) create mode 100644 core/Workflows/task-execution/.meta/task-target-workflow.diagram.json delete mode 100644 core/Workflows/task-execution/src/task-execution-test-workflow/NotificationMapping.csx diff --git a/api-tests/task-execution/postman-task-execution-collection.json b/api-tests/task-execution/postman-task-execution-collection.json index cbdd051..f21c6e3 100644 --- a/api-tests/task-execution/postman-task-execution-collection.json +++ b/api-tests/task-execution/postman-task-execution-collection.json @@ -156,7 +156,21 @@ ] }, { - "name": "Adim 2: Hedef workflow durumu", + "name": "Adim 2: PATCH manual-complete-target", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{targetInstanceId}}/transitions/manual-complete-target", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{targetWorkflow}}", "instances", "{{targetInstanceId}}", "transitions", "manual-complete-target"] + }, + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + }, + "response": [] + }, + { + "name": "Adim 3: Hedef workflow durumu", "request": { "method": "GET", "header": [], diff --git a/api-tests/task-execution/task-execution.http b/api-tests/task-execution/task-execution.http index b7a01c4..70975c5 100644 --- a/api-tests/task-execution/task-execution.http +++ b/api-tests/task-execution/task-execution.http @@ -17,7 +17,7 @@ ############################################################################### # BOLUM 1 — Happy path: Ana task execution zinciri # HTTP -> Script -> Cross workflow -> Start Flow (11) -> Get Instance Data (13) -# -> Notification (10) -> TriggerTransition (12) -> SubProcess (14) +# -> Notification (10, mapping.type G) -> TriggerTransition (12) -> SubProcess (14) # -> GetInstances (15) -> Human Task (5) -> PATCH approve -> Completed # Requires: MocklabBaseUrl config, Mocklab seeded (POST /api/test/process) ############################################################################### @@ -41,7 +41,7 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/ ### B1 — Adim 3: Instance data (notification / trigger / subprocess / getInstances bayraklari) # Beklenen: testId, httpTaskCompleted, scriptProcessed, crossWorkflowCompleted, # startFlowCompleted, startedInstanceId, getInstanceDataCompleted, -# taskResults.notification.completed, taskResults.triggerTransition.completed, +# taskResults.triggerTransition.completed, # taskResults.subprocess.completed, taskResults.getInstances.completed, # taskResults.humanTask.completed GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/data @@ -75,7 +75,13 @@ Content-Type: application/json @targetInstanceId = {{createTargetInstance.response.body.$.id}} -### B2 — Adim 2: Hedef workflow durumu (target-completed olmali) +### B2 — Adim 2: Hedefde manuel gecis (DirectTriggerTask ile ayni anahtar: manual-complete-target) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{targetInstanceId}}/transitions/manual-complete-target +Content-Type: application/json + +{} + +### B2 — Adim 3: Hedef workflow durumu (target-completed olmali) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{targetInstanceId}}/functions/state ############################################################################### diff --git a/core.link.json b/core.link.json index 6055407..10dd625 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-04-30T11:55:47.337Z" + "importedAt": "2026-04-30T12:27:03.890Z" } \ No newline at end of file diff --git a/core/Tasks/task-execution/task-execution-test-workflow/trigger-transition-task.json b/core/Tasks/task-execution/task-execution-test-workflow/trigger-transition-task.json index 4baa617..459bb68 100644 --- a/core/Tasks/task-execution/task-execution-test-workflow/trigger-transition-task.json +++ b/core/Tasks/task-execution/task-execution-test-workflow/trigger-transition-task.json @@ -10,7 +10,7 @@ "config": { "domain": "core", "flow": "task-target-workflow", - "transitionName": "auto-complete-target" + "transitionName": "manual-complete-target" } } } diff --git a/core/Workflows/task-execution/.meta/task-target-workflow.diagram.json b/core/Workflows/task-execution/.meta/task-target-workflow.diagram.json new file mode 100644 index 0000000..187ef01 --- /dev/null +++ b/core/Workflows/task-execution/.meta/task-target-workflow.diagram.json @@ -0,0 +1,16 @@ +{ + "nodePos": { + "__start__": { + "x": 12, + "y": 42 + }, + "target-initial": { + "x": 220, + "y": 150 + }, + "target-completed": { + "x": 690, + "y": 0 + } + } +} \ No newline at end of file diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstanceDataMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstanceDataMapping.csx index be75e33..afbfe35 100644 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstanceDataMapping.csx +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstanceDataMapping.csx @@ -14,9 +14,9 @@ public class GetInstanceDataMapping : ScriptBase, IMapping if (HasProperty(data, "startedInstanceId")) { - var instanceId = data.startedInstanceId?.ToString(); - getDataTask.SetInstance(instanceId); - LogInformation($"GetInstanceDataMapping InputHandler: target instanceId={instanceId}"); + var targetId = data.startedInstanceId?.ToString(); + getDataTask.SetInstance(targetId); + LogInformation($"GetInstanceDataMapping InputHandler: target id={targetId}"); } return new ScriptResponse(); diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/NotificationMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/NotificationMapping.csx deleted file mode 100644 index 05e0def..0000000 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/NotificationMapping.csx +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Dynamic; -using System.Threading.Tasks; -using BBT.Workflow.Scripting; -using BBT.Workflow.Definitions; - -public class NotificationMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public Task OutputHandler(ScriptContext context) - { - var data = context.Instance.Data; - dynamic result = new ExpandoObject(); - - if (HasProperty(data, "testId")) result.testId = data.testId; - if (HasProperty(data, "startedAt")) result.startedAt = data.startedAt; - if (HasProperty(data, "httpTaskCompleted")) result.httpTaskCompleted = data.httpTaskCompleted; - if (HasProperty(data, "httpStatusCode")) result.httpStatusCode = data.httpStatusCode; - if (HasProperty(data, "httpIsSuccess")) result.httpIsSuccess = data.httpIsSuccess; - if (HasProperty(data, "processId")) result.processId = data.processId; - if (HasProperty(data, "scriptProcessed")) result.scriptProcessed = data.scriptProcessed; - if (HasProperty(data, "scriptProcessedAt")) result.scriptProcessedAt = data.scriptProcessedAt; - if (HasProperty(data, "crossWorkflowCompleted")) result.crossWorkflowCompleted = data.crossWorkflowCompleted; - if (HasProperty(data, "crossWorkflowAt")) result.crossWorkflowAt = data.crossWorkflowAt; - if (HasProperty(data, "startFlowCompleted")) result.startFlowCompleted = data.startFlowCompleted; - if (HasProperty(data, "startFlowIsSuccess")) result.startFlowIsSuccess = data.startFlowIsSuccess; - if (HasProperty(data, "startedInstanceId")) result.startedInstanceId = data.startedInstanceId; - if (HasProperty(data, "getInstanceDataCompleted")) result.getInstanceDataCompleted = data.getInstanceDataCompleted; - if (HasProperty(data, "getInstanceDataIsSuccess")) result.getInstanceDataIsSuccess = data.getInstanceDataIsSuccess; - if (HasProperty(data, "remoteInstanceData")) result.remoteInstanceData = data.remoteInstanceData; - - result.taskResults = new ExpandoObject(); - if (HasProperty(data, "taskResults")) - { - if (HasProperty(data.taskResults, "notification")) result.taskResults.notification = data.taskResults.notification; - if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; - if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; - if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; - if (HasProperty(data.taskResults, "humanTask")) result.taskResults.humanTask = data.taskResults.humanTask; - } - - result.taskResults.notification = new ExpandoObject(); - result.taskResults.notification.completed = true; - result.taskResults.notification.executedAt = DateTime.UtcNow.ToString("o"); - - LogInformation("NotificationMapping completed"); - return Task.FromResult(new ScriptResponse { Data = result }); - } -} diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/StartFlowMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/StartFlowMapping.csx index 7da51ff..ea55ecb 100644 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/StartFlowMapping.csx +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/StartFlowMapping.csx @@ -39,8 +39,13 @@ public class StartFlowMapping : ScriptBase, IMapping result.startFlowCompleted = true; result.startFlowIsSuccess = taskResponse.isSuccess; - if (responseBody != null && HasProperty(responseBody, "instanceId")) - result.startedInstanceId = responseBody.instanceId; + if (responseBody != null) + { + if (HasProperty(responseBody, "id")) + result.startedInstanceId = responseBody.id; + else if (HasProperty(responseBody, "instanceId")) + result.startedInstanceId = responseBody.instanceId; + } LogInformation($"StartFlowMapping OutputHandler: isSuccess={taskResponse.isSuccess}"); return new ScriptResponse { Data = result }; diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx index a4fd0a0..dd10dbc 100644 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx @@ -3,11 +3,22 @@ using System.Dynamic; using System.Threading.Tasks; using BBT.Workflow.Scripting; using BBT.Workflow.Definitions; +using BBT.Workflow.Scripting.Functions; public class TriggerTransitionMapping : ScriptBase, IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) { + var directTrigger = (task as DirectTriggerTask)!; + var data = context.Instance.Data; + + if (HasProperty(data, "startedInstanceId")) + { + var targetId = data.startedInstanceId?.ToString(); + directTrigger.SetInstance(targetId!); + LogInformation($"TriggerTransitionMapping InputHandler: SetInstance {targetId}"); + } + return Task.FromResult(new ScriptResponse()); } diff --git a/core/Workflows/task-execution/task-execution-test-workflow.json b/core/Workflows/task-execution/task-execution-test-workflow.json index a6fba87..123ce5d 100644 --- a/core/Workflows/task-execution/task-execution-test-workflow.json +++ b/core/Workflows/task-execution/task-execution-test-workflow.json @@ -267,7 +267,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/StartFlowMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBTdGFydEZsb3dNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBzdGFydFRhc2sgPSAodGFzayBhcyBTdGFydFRhc2spITsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKCiAgICAgICAgdmFyIGJvZHkgPSBuZXcKICAgICAgICB7CiAgICAgICAgICAgIHNvdXJjZSA9ICJ0YXNrLWV4ZWN1dGlvbi10ZXN0IiwKICAgICAgICAgICAgcGFyZW50SW5zdGFuY2VJZCA9IGNvbnRleHQuSW5zdGFuY2UuSWQKICAgICAgICB9OwogICAgICAgIHN0YXJ0VGFzay5TZXRCb2R5KGJvZHkpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU3RhcnRGbG93TWFwcGluZyBJbnB1dEhhbmRsZXI6IGJvZHkgc2V0IGZvciB0YXJnZXQgd29ya2Zsb3ciKTsKICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlKCk7CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIHZhciB0YXNrUmVzcG9uc2UgPSBjb250ZXh0LkJvZHk7CiAgICAgICAgdmFyIHJlc3BvbnNlQm9keSA9IHRhc2tSZXNwb25zZS5kYXRhOwoKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCIpKSByZXN1bHQuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZDsKCiAgICAgICAgcmVzdWx0LnN0YXJ0Rmxvd0NvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnN0YXJ0Rmxvd0lzU3VjY2VzcyA9IHRhc2tSZXNwb25zZS5pc1N1Y2Nlc3M7CgogICAgICAgIGlmIChyZXNwb25zZUJvZHkgIT0gbnVsbCAmJiBIYXNQcm9wZXJ0eShyZXNwb25zZUJvZHksICJpbnN0YW5jZUlkIikpCiAgICAgICAgICAgIHJlc3VsdC5zdGFydGVkSW5zdGFuY2VJZCA9IHJlc3BvbnNlQm9keS5pbnN0YW5jZUlkOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigkIlN0YXJ0Rmxvd01hcHBpbmcgT3V0cHV0SGFuZGxlcjogaXNTdWNjZXNzPXt0YXNrUmVzcG9uc2UuaXNTdWNjZXNzfSIpOwogICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH07CiAgICB9Cn0K" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBTdGFydEZsb3dNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBzdGFydFRhc2sgPSAodGFzayBhcyBTdGFydFRhc2spITsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKCiAgICAgICAgdmFyIGJvZHkgPSBuZXcKICAgICAgICB7CiAgICAgICAgICAgIHNvdXJjZSA9ICJ0YXNrLWV4ZWN1dGlvbi10ZXN0IiwKICAgICAgICAgICAgcGFyZW50SW5zdGFuY2VJZCA9IGNvbnRleHQuSW5zdGFuY2UuSWQKICAgICAgICB9OwogICAgICAgIHN0YXJ0VGFzay5TZXRCb2R5KGJvZHkpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU3RhcnRGbG93TWFwcGluZyBJbnB1dEhhbmRsZXI6IGJvZHkgc2V0IGZvciB0YXJnZXQgd29ya2Zsb3ciKTsKICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlKCk7CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIHZhciB0YXNrUmVzcG9uc2UgPSBjb250ZXh0LkJvZHk7CiAgICAgICAgdmFyIHJlc3BvbnNlQm9keSA9IHRhc2tSZXNwb25zZS5kYXRhOwoKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCIpKSByZXN1bHQuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZDsKCiAgICAgICAgcmVzdWx0LnN0YXJ0Rmxvd0NvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnN0YXJ0Rmxvd0lzU3VjY2VzcyA9IHRhc2tSZXNwb25zZS5pc1N1Y2Nlc3M7CgogICAgICAgIGlmIChyZXNwb25zZUJvZHkgIT0gbnVsbCkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShyZXNwb25zZUJvZHksICJpZCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnN0YXJ0ZWRJbnN0YW5jZUlkID0gcmVzcG9uc2VCb2R5LmlkOwogICAgICAgICAgICBlbHNlIGlmIChIYXNQcm9wZXJ0eShyZXNwb25zZUJvZHksICJpbnN0YW5jZUlkIikpCiAgICAgICAgICAgICAgICByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSByZXNwb25zZUJvZHkuaW5zdGFuY2VJZDsKICAgICAgICB9CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCQiU3RhcnRGbG93TWFwcGluZyBPdXRwdXRIYW5kbGVyOiBpc1N1Y2Nlc3M9e3Rhc2tSZXNwb25zZS5pc1N1Y2Nlc3N9Iik7CiAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfTsKICAgIH0KfQo=" } } ], @@ -324,7 +324,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/GetInstanceDataMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBHZXRJbnN0YW5jZURhdGFNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBnZXREYXRhVGFzayA9ICh0YXNrIGFzIEdldEluc3RhbmNlRGF0YVRhc2spITsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKQogICAgICAgIHsKICAgICAgICAgICAgdmFyIGluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkPy5Ub1N0cmluZygpOwogICAgICAgICAgICBnZXREYXRhVGFzay5TZXRJbnN0YW5jZShpbnN0YW5jZUlkKTsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oJCJHZXRJbnN0YW5jZURhdGFNYXBwaW5nIElucHV0SGFuZGxlcjogdGFyZ2V0IGluc3RhbmNlSWQ9e2luc3RhbmNlSWR9Iik7CiAgICAgICAgfQoKICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlKCk7CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIHZhciB0YXNrUmVzcG9uc2UgPSBjb250ZXh0LkJvZHk7CiAgICAgICAgdmFyIHJlc3BvbnNlQm9keSA9IHRhc2tSZXNwb25zZS5kYXRhOwoKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCIpKSByZXN1bHQuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0NvbXBsZXRlZCIpKSByZXN1bHQuc3RhcnRGbG93Q29tcGxldGVkID0gZGF0YS5zdGFydEZsb3dDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwoKICAgICAgICByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gdGFza1Jlc3BvbnNlLmlzU3VjY2VzczsKCiAgICAgICAgaWYgKHJlc3BvbnNlQm9keSAhPSBudWxsKQogICAgICAgICAgICByZXN1bHQucmVtb3RlSW5zdGFuY2VEYXRhID0gcmVzcG9uc2VCb2R5OwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkdldEluc3RhbmNlRGF0YU1hcHBpbmcgT3V0cHV0SGFuZGxlcjogaXNTdWNjZXNzPXt0YXNrUmVzcG9uc2UuaXNTdWNjZXNzfSIpOwogICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH07CiAgICB9Cn0K" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBHZXRJbnN0YW5jZURhdGFNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBnZXREYXRhVGFzayA9ICh0YXNrIGFzIEdldEluc3RhbmNlRGF0YVRhc2spITsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKQogICAgICAgIHsKICAgICAgICAgICAgdmFyIHRhcmdldElkID0gZGF0YS5zdGFydGVkSW5zdGFuY2VJZD8uVG9TdHJpbmcoKTsKICAgICAgICAgICAgZ2V0RGF0YVRhc2suU2V0SW5zdGFuY2UodGFyZ2V0SWQpOwogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkdldEluc3RhbmNlRGF0YU1hcHBpbmcgSW5wdXRIYW5kbGVyOiB0YXJnZXQgaWQ9e3RhcmdldElkfSIpOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZSgpOwogICAgfQoKICAgIHB1YmxpYyBhc3luYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICB2YXIgdGFza1Jlc3BvbnNlID0gY29udGV4dC5Cb2R5OwogICAgICAgIHZhciByZXNwb25zZUJvZHkgPSB0YXNrUmVzcG9uc2UuZGF0YTsKCiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJwcm9jZXNzSWQiKSkgcmVzdWx0LnByb2Nlc3NJZCA9IGRhdGEucHJvY2Vzc0lkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkIikpIHJlc3VsdC5zY3JpcHRQcm9jZXNzZWQgPSBkYXRhLnNjcmlwdFByb2Nlc3NlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyb3NzV29ya2Zsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQgPSBkYXRhLmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydEZsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LnN0YXJ0Rmxvd0NvbXBsZXRlZCA9IGRhdGEuc3RhcnRGbG93Q29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEluc3RhbmNlSWQiKSkgcmVzdWx0LnN0YXJ0ZWRJbnN0YW5jZUlkID0gZGF0YS5zdGFydGVkSW5zdGFuY2VJZDsKCiAgICAgICAgcmVzdWx0LmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LmdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyA9IHRhc2tSZXNwb25zZS5pc1N1Y2Nlc3M7CgogICAgICAgIGlmIChyZXNwb25zZUJvZHkgIT0gbnVsbCkKICAgICAgICAgICAgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IHJlc3BvbnNlQm9keTsKCiAgICAgICAgTG9nSW5mb3JtYXRpb24oJCJHZXRJbnN0YW5jZURhdGFNYXBwaW5nIE91dHB1dEhhbmRsZXI6IGlzU3VjY2Vzcz17dGFza1Jlc3BvbnNlLmlzU3VjY2Vzc30iKTsKICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9OwogICAgfQp9Cg==" } } ], @@ -380,8 +380,7 @@ "flow": "sys-tasks" }, "mapping": { - "location": "./src/task-execution-test-workflow/NotificationMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIE5vdGlmaWNhdGlvbk1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyb3NzV29ya2Zsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQgPSBkYXRhLmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcm9zc1dvcmtmbG93QXQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dBdCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0F0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJodW1hblRhc2siKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IGRhdGEudGFza1Jlc3VsdHMuaHVtYW5UYXNrOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbi5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24uZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiTm90aWZpY2F0aW9uTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" + "type": "G" } } ], @@ -438,7 +437,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/TriggerTransitionMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFRyaWdnZXJUcmFuc2l0aW9uTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkQXQiKSkgcmVzdWx0LnN0YXJ0ZWRBdCA9IGRhdGEuc3RhcnRlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBTdGF0dXNDb2RlIikpIHJlc3VsdC5odHRwU3RhdHVzQ29kZSA9IGRhdGEuaHR0cFN0YXR1c0NvZGU7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwSXNTdWNjZXNzIikpIHJlc3VsdC5odHRwSXNTdWNjZXNzID0gZGF0YS5odHRwSXNTdWNjZXNzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicHJvY2Vzc0lkIikpIHJlc3VsdC5wcm9jZXNzSWQgPSBkYXRhLnByb2Nlc3NJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInNjcmlwdFByb2Nlc3NlZCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkID0gZGF0YS5zY3JpcHRQcm9jZXNzZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWRBdCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkQXQgPSBkYXRhLnNjcmlwdFByb2Nlc3NlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCIpKSByZXN1bHQuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyb3NzV29ya2Zsb3dBdCIpKSByZXN1bHQuY3Jvc3NXb3JrZmxvd0F0ID0gZGF0YS5jcm9zc1dvcmtmbG93QXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydEZsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LnN0YXJ0Rmxvd0NvbXBsZXRlZCA9IGRhdGEuc3RhcnRGbG93Q29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93SXNTdWNjZXNzIikpIHJlc3VsdC5zdGFydEZsb3dJc1N1Y2Nlc3MgPSBkYXRhLnN0YXJ0Rmxvd0lzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRJbnN0YW5jZUlkIikpIHJlc3VsdC5zdGFydGVkSW5zdGFuY2VJZCA9IGRhdGEuc3RhcnRlZEluc3RhbmNlSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJnZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQiKSkgcmVzdWx0LmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZCA9IGRhdGEuZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3MgPSBkYXRhLmdldEluc3RhbmNlRGF0YUlzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInJlbW90ZUluc3RhbmNlRGF0YSIpKSByZXN1bHQucmVtb3RlSW5zdGFuY2VEYXRhID0gZGF0YS5yZW1vdGVJbnN0YW5jZURhdGE7CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0YXNrUmVzdWx0cyIpKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInRyaWdnZXJUcmFuc2l0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAic3VicHJvY2VzcyIpKSByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2VzcyA9IGRhdGEudGFza1Jlc3VsdHMuc3VicHJvY2VzczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJnZXRJbnN0YW5jZXMiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IGRhdGEudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImh1bWFuVGFzayIpKSByZXN1bHQudGFza1Jlc3VsdHMuaHVtYW5UYXNrID0gZGF0YS50YXNrUmVzdWx0cy5odW1hblRhc2s7CiAgICAgICAgfQoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbi5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbi5leGVjdXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCJUcmlnZ2VyVHJhbnNpdGlvbk1hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBUcmlnZ2VyVHJhbnNpdGlvbk1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRpcmVjdFRyaWdnZXIgPSAodGFzayBhcyBEaXJlY3RUcmlnZ2VyVGFzaykhOwogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRJbnN0YW5jZUlkIikpCiAgICAgICAgewogICAgICAgICAgICB2YXIgdGFyZ2V0SWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkPy5Ub1N0cmluZygpOwogICAgICAgICAgICBkaXJlY3RUcmlnZ2VyLlNldEluc3RhbmNlKHRhcmdldElkISk7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCQiVHJpZ2dlclRyYW5zaXRpb25NYXBwaW5nIElucHV0SGFuZGxlcjogU2V0SW5zdGFuY2Uge3RhcmdldElkfSIpOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyb3NzV29ya2Zsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQgPSBkYXRhLmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcm9zc1dvcmtmbG93QXQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dBdCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0F0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJodW1hblRhc2siKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IGRhdGEudGFza1Jlc3VsdHMuaHVtYW5UYXNrOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24uY29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24uZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiVHJpZ2dlclRyYW5zaXRpb25NYXBwaW5nIGNvbXBsZXRlZCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" } } ], diff --git a/core/Workflows/task-execution/task-target-workflow.json b/core/Workflows/task-execution/task-target-workflow.json index a30e195..30ab361 100644 --- a/core/Workflows/task-execution/task-target-workflow.json +++ b/core/Workflows/task-execution/task-target-workflow.json @@ -65,24 +65,20 @@ "onExits": [], "transitions": [ { - "key": "auto-complete-target", + "key": "manual-complete-target", "target": "target-completed", - "triggerType": 1, + "triggerType": 0, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Auto Complete Target" + "label": "Complete Target (manual / DirectTriggerTask)" }, { "language": "tr-TR", - "label": "Otomatik Tamamla" + "label": "Hedefi Tamamla (manuel / DirectTriggerTask)" } ], - "rule": { - "location": "./src/shared/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" - }, "onExecutionTasks": [] } ] diff --git a/doc/integration-test-documentation.md b/doc/integration-test-documentation.md index 7372fe1..4848e9c 100644 --- a/doc/integration-test-documentation.md +++ b/doc/integration-test-documentation.md @@ -363,7 +363,7 @@ task-execution-test-workflow (F) │ └── Transitions: auto-to-notification → notification-state │ ├── notification-state -│ ├── onEntries: notification-task (type:10) + NotificationMapping.csx +│ ├── onEntries: notification-task (type:10) + mapping.type G │ └── Transitions: auto-to-trigger-transition → trigger-transition-state │ ├── trigger-transition-state @@ -387,7 +387,7 @@ task-execution-test-workflow (F) task-target-workflow (F) │ ├── [startTransition] start-target → target-initial -├── target-initial → auto-complete-target → target-completed +├── target-initial — manual-complete-target → target-completed └── target-completed ``` @@ -416,8 +416,8 @@ extended-tasks-test-workflow (F) | Script Task (type:7) | C# script data | task-exec-script-task + ScriptProcessMapping | | StartFlow Task (type:11) | Hedef workflow baslatma | start-flow-task + StartFlowMapping | | GetInstanceData Task (type:13) | Baska instance datasi | get-instance-data-task + GetInstanceDataMapping | -| Notification Task (type:10) | Bildirim metadata | notification-task + NotificationMapping | -| TriggerTransition Task (type:12) | Hedef instance'da transition | trigger-transition-task + TriggerTransitionMapping | +| Notification Task (type:10) | Platform mapping G (script yok) | notification-task + mapping `{ "type": "G" }` | +| TriggerTransition Task (type:12) | Hedef instance + manuel gecis | trigger-transition-task + TriggerTransitionMapping (SetInstance + `manual-complete-target`) | | SubProcess Task (type:14) | Alt surec baslatma | subprocess-task + SubProcessMapping | | GetInstances Task (type:15) | Instance listesi | get-instances-task + GetInstancesMapping | | Human Task (type:5) | Manuel `approve-human-task` | human-task + HumanTaskMapping | diff --git a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs index a02e287..37cbacb 100644 --- a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs +++ b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs @@ -15,7 +15,9 @@ internal static class TaskExecutionMainWorkflowInstanceDataAssertions /// /// Human-task beklemeden önce: HTTP, script, cross-workflow, start-flow, get-instance-data, - /// notification, trigger, subprocess, get-instances ve human-task onEntry mapping bayrakları. + /// trigger, subprocess, get-instances ve human-task onEntry mapping bayrakları. + /// Notification task mapping.type: G kullandığı için + /// taskResults.notification entegrasyon assert'ine dahil edilmez. /// public static void AssertWhileWaitingOnHumanTask(JsonElement attributes) { @@ -51,12 +53,6 @@ public static void AssertWhileWaitingOnHumanTask(JsonElement attributes) ContractHint ); - JsonElementAssertions.AssertNestedPropertyTrue( - attributes, - new[] { "taskResults", "notification" }, - "completed", - ContractHint - ); JsonElementAssertions.AssertNestedPropertyTrue( attributes, new[] { "taskResults", "triggerTransition" }, diff --git a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs index d2029d5..1ad0365 100644 --- a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs +++ b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs @@ -60,6 +60,25 @@ await _mainWorkflow.WaitForStateAsync( var attrsBefore = await GetAttributesAsync(MainWorkflowKey, instanceId); TaskExecutionMainWorkflowInstanceDataAssertions.AssertWhileWaitingOnHumanTask(attrsBefore); + // StartFlow + DirectTrigger zincirinin sadece parent instance'a `startedInstanceId` yazmış olması yeterli değildir. + // Hedef workflow'un `functions/state` çağrısıyla; (1) StartTask yeni bir instance açtı, (2) DirectTriggerTask + // hedefin manuel `manual-complete-target` geçişini gerçekten tetikledi, doğrulanır. + var startedTargetId = attrsBefore.GetProperty("startedInstanceId").GetString(); + Assert.False( + string.IsNullOrWhiteSpace(startedTargetId), + "startedInstanceId should be a non-empty string from StartFlowMapping output. Because startflow task should start a workflow." + ); + + var targetStateAfterTrigger = await _targetWorkflow.GetStateFunctionBodyAsync( + startedTargetId!, + headers: null + ); + Assert.Equal( + "target-completed", + StateFunctionJson.ExtractStateName(targetStateAfterTrigger) + ); + Assert.Equal("C", StateFunctionJson.ExtractStatus(targetStateAfterTrigger)); + await _mainWorkflow.RunTransitionAsync( instanceId, "approve-human-task", @@ -94,6 +113,19 @@ public async Task B2_TargetWorkflow_HappyPath_ReachesTargetCompleted() } ); + await _targetWorkflow.WaitForStateAsync( + instanceId, + "target-initial", + TimeSpan.FromSeconds(30) + ); + + await _targetWorkflow.RunTransitionAsync( + instanceId, + "manual-complete-target", + headers: null, + transitionBody: new { } + ); + await _targetWorkflow.WaitForStateAsync( instanceId, "target-completed", From 6e05bebc4de4b9e31dd8655c6be0470b53d65209 Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Thu, 30 Apr 2026 15:59:37 +0300 Subject: [PATCH 08/24] subprocess false positives are cleared. --- .../postman-task-execution-collection.json | 56 +++++++++++++++++++ api-tests/task-execution/task-execution.http | 21 ++++++- .../StartFlowMapping.csx | 9 ++- .../SubProcessMapping.csx | 31 ++++++++++ .../task-execution-test-workflow.json | 6 +- doc/integration-test-documentation.md | 4 +- .../TaskExecutionInstanceDataAssertions.cs | 7 +++ .../TaskExecutionTestWorkflowTests.cs | 48 ++++++++++++++++ 8 files changed, 172 insertions(+), 10 deletions(-) diff --git a/api-tests/task-execution/postman-task-execution-collection.json b/api-tests/task-execution/postman-task-execution-collection.json index f21c6e3..181a017 100644 --- a/api-tests/task-execution/postman-task-execution-collection.json +++ b/api-tests/task-execution/postman-task-execution-collection.json @@ -75,6 +75,62 @@ "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{taskExecInstanceId}}", "functions", "data"] } }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "if (jsonData && jsonData.startedInstanceId) {", + " pm.collectionVariables.set(\"taskExecStartedTargetId\", jsonData.startedInstanceId);", + "}", + "if (jsonData && jsonData.subprocessInstanceId) {", + " pm.collectionVariables.set(\"taskExecSubprocessId\", jsonData.subprocessInstanceId);", + "}" + ] + } + } + ] + }, + { + "name": "Adim 3a: StartTask hedef instance state", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{taskExecStartedTargetId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{targetWorkflow}}", "instances", "{{taskExecStartedTargetId}}", "functions", "state"] + } + }, + "response": [] + }, + { + "name": "Adim 3b: SubProcessTask hedef instance state", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{taskExecSubprocessId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{targetWorkflow}}", "instances", "{{taskExecSubprocessId}}", "functions", "state"] + } + }, + "response": [] + }, + { + "name": "Adim 3c: SubProcessTask hedef instance attributes (parentInstanceId/source/note)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{taskExecSubprocessId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{targetWorkflow}}", "instances", "{{taskExecSubprocessId}}"] + } + }, "response": [] }, { diff --git a/api-tests/task-execution/task-execution.http b/api-tests/task-execution/task-execution.http index 70975c5..44e72eb 100644 --- a/api-tests/task-execution/task-execution.http +++ b/api-tests/task-execution/task-execution.http @@ -40,12 +40,29 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/ ### B1 — Adim 3: Instance data (notification / trigger / subprocess / getInstances bayraklari) # Beklenen: testId, httpTaskCompleted, scriptProcessed, crossWorkflowCompleted, -# startFlowCompleted, startedInstanceId, getInstanceDataCompleted, -# taskResults.triggerTransition.completed, +# startFlowCompleted, startedInstanceId (StartTask sonucu hedef workflow id), +# subprocessInstanceId (SubProcessTask sonucu hedef workflow id), +# getInstanceDataCompleted, taskResults.triggerTransition.completed, # taskResults.subprocess.completed, taskResults.getInstances.completed, # taskResults.humanTask.completed GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/data +### B1 — Adim 3a: StartTask ile baslatilan hedef instance functions/state (target-completed olmali) +# startedInstanceId'yi B1 Adim 3 cikisindan kopyalayip asagidaki degiskene yapistir. +# (Postman koleksiyonu bunu otomatik degisken olarak set eder.) +@taskExecStartedTargetId = REPLACE_WITH_startedInstanceId +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{taskExecStartedTargetId}}/functions/state + +### B1 — Adim 3b: SubProcessTask ile acilan hedef instance functions/state +# Subprocess fire-and-forget'tir; state target-initial (Active) veya target-completed olabilir. +@taskExecSubprocessId = REPLACE_WITH_subprocessInstanceId +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{taskExecSubprocessId}}/functions/state + +### B1 — Adim 3c: SubProcessTask ile acilan hedef instance attributes +# Beklenen: source = "task-execution-test", parentInstanceId = ana workflow instance id, +# note alani SubProcessMapping InputHandler'da SetBody ile yazilmali. +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{taskExecSubprocessId}} + ### B1 — Adim 4: Human task onayla (manuel transition) PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/transitions/approve-human-task Content-Type: application/json diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/StartFlowMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/StartFlowMapping.csx index ea55ecb..175a4e3 100644 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/StartFlowMapping.csx +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/StartFlowMapping.csx @@ -9,17 +9,20 @@ public class StartFlowMapping : ScriptBase, IMapping { public async Task InputHandler(WorkflowTask task, ScriptContext context) { + // StartTask (tip 11) yeni bir hedef workflow instance'i acar. + // Body uzerinden hedef instance data'sina parent referansi ve kaynak notu yaziyoruz ki + // testler hedef GetInstance attributes'unda parent zincirini dogrulayabilsin. var startTask = (task as StartTask)!; - var data = context.Instance.Data; var body = new { source = "task-execution-test", - parentInstanceId = context.Instance.Id + parentInstanceId = context.Instance.Id, + note = "this is a startflow from task-execution-test-workflow" }; startTask.SetBody(body); - LogInformation("StartFlowMapping InputHandler: body set for target workflow"); + LogInformation("StartFlowMapping InputHandler: body set with parentInstanceId/source/note"); return new ScriptResponse(); } diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx index a1b6d3f..2b6a3df 100644 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx @@ -3,17 +3,35 @@ using System.Dynamic; using System.Threading.Tasks; using BBT.Workflow.Scripting; using BBT.Workflow.Definitions; +using BBT.Workflow.Scripting.Functions; public class SubProcessMapping : ScriptBase, IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) { + // SubProcessTask (tip 14) fire-and-forget bir alt instance acar. + // Body uzerinden subprocess'in instance data'sina parent referansi ve kaynak notu yaziyoruz; + // testler bu alanlari subprocess GetInstance attributes'unda dogrulayabilsin. + var subProcessTask = (task as SubProcessTask)!; + + var body = new + { + source = "task-execution-test", + parentInstanceId = context.Instance.Id, + note = "this is a subprocess started from task-execution-test-workflow" + }; + subProcessTask.SetBody(body); + + LogInformation("SubProcessMapping InputHandler: body set with parentInstanceId/source/note"); return Task.FromResult(new ScriptResponse()); } public Task OutputHandler(ScriptContext context) { var data = context.Instance.Data; + var taskResponse = context.Body; + var responseBody = taskResponse?.data; + dynamic result = new ExpandoObject(); if (HasProperty(data, "testId")) result.testId = data.testId; @@ -47,6 +65,19 @@ public class SubProcessMapping : ScriptBase, IMapping result.taskResults.subprocess.completed = true; result.taskResults.subprocess.executedAt = DateTime.UtcNow.ToString("o"); + // SubProcess yaniti runtime dokumanina gore data.id (yeni instance id) doner. + // Eski yanitlarda data.instanceId gorulebilir; ikisini de guvenli sekilde dene. + if (responseBody != null) + { + if (HasProperty(responseBody, "id")) + result.subprocessInstanceId = responseBody.id; + else if (HasProperty(responseBody, "instanceId")) + result.subprocessInstanceId = responseBody.instanceId; + } + + if (taskResponse != null && HasProperty(taskResponse, "isSuccess")) + result.taskResults.subprocess.isSuccess = taskResponse.isSuccess; + LogInformation("SubProcessMapping completed"); return Task.FromResult(new ScriptResponse { Data = result }); } diff --git a/core/Workflows/task-execution/task-execution-test-workflow.json b/core/Workflows/task-execution/task-execution-test-workflow.json index 123ce5d..f916672 100644 --- a/core/Workflows/task-execution/task-execution-test-workflow.json +++ b/core/Workflows/task-execution/task-execution-test-workflow.json @@ -267,7 +267,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/StartFlowMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBTdGFydEZsb3dNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBzdGFydFRhc2sgPSAodGFzayBhcyBTdGFydFRhc2spITsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKCiAgICAgICAgdmFyIGJvZHkgPSBuZXcKICAgICAgICB7CiAgICAgICAgICAgIHNvdXJjZSA9ICJ0YXNrLWV4ZWN1dGlvbi10ZXN0IiwKICAgICAgICAgICAgcGFyZW50SW5zdGFuY2VJZCA9IGNvbnRleHQuSW5zdGFuY2UuSWQKICAgICAgICB9OwogICAgICAgIHN0YXJ0VGFzay5TZXRCb2R5KGJvZHkpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU3RhcnRGbG93TWFwcGluZyBJbnB1dEhhbmRsZXI6IGJvZHkgc2V0IGZvciB0YXJnZXQgd29ya2Zsb3ciKTsKICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlKCk7CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIHZhciB0YXNrUmVzcG9uc2UgPSBjb250ZXh0LkJvZHk7CiAgICAgICAgdmFyIHJlc3BvbnNlQm9keSA9IHRhc2tSZXNwb25zZS5kYXRhOwoKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCIpKSByZXN1bHQuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZDsKCiAgICAgICAgcmVzdWx0LnN0YXJ0Rmxvd0NvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnN0YXJ0Rmxvd0lzU3VjY2VzcyA9IHRhc2tSZXNwb25zZS5pc1N1Y2Nlc3M7CgogICAgICAgIGlmIChyZXNwb25zZUJvZHkgIT0gbnVsbCkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShyZXNwb25zZUJvZHksICJpZCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnN0YXJ0ZWRJbnN0YW5jZUlkID0gcmVzcG9uc2VCb2R5LmlkOwogICAgICAgICAgICBlbHNlIGlmIChIYXNQcm9wZXJ0eShyZXNwb25zZUJvZHksICJpbnN0YW5jZUlkIikpCiAgICAgICAgICAgICAgICByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSByZXNwb25zZUJvZHkuaW5zdGFuY2VJZDsKICAgICAgICB9CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCQiU3RhcnRGbG93TWFwcGluZyBPdXRwdXRIYW5kbGVyOiBpc1N1Y2Nlc3M9e3Rhc2tSZXNwb25zZS5pc1N1Y2Nlc3N9Iik7CiAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfTsKICAgIH0KfQo=" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBTdGFydEZsb3dNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIC8vIFN0YXJ0VGFzayAodGlwIDExKSB5ZW5pIGJpciBoZWRlZiB3b3JrZmxvdyBpbnN0YW5jZSdpIGFjYXIuCiAgICAgICAgLy8gQm9keSB1emVyaW5kZW4gaGVkZWYgaW5zdGFuY2UgZGF0YSdzaW5hIHBhcmVudCByZWZlcmFuc2kgdmUga2F5bmFrIG5vdHUgeWF6aXlvcnV6IGtpCiAgICAgICAgLy8gdGVzdGxlciBoZWRlZiBHZXRJbnN0YW5jZSBhdHRyaWJ1dGVzJ3VuZGEgcGFyZW50IHppbmNpcmluaSBkb2dydWxheWFiaWxzaW4uCiAgICAgICAgdmFyIHN0YXJ0VGFzayA9ICh0YXNrIGFzIFN0YXJ0VGFzaykhOwoKICAgICAgICB2YXIgYm9keSA9IG5ldwogICAgICAgIHsKICAgICAgICAgICAgc291cmNlID0gInRhc2stZXhlY3V0aW9uLXRlc3QiLAogICAgICAgICAgICBwYXJlbnRJbnN0YW5jZUlkID0gY29udGV4dC5JbnN0YW5jZS5JZCwKICAgICAgICAgICAgbm90ZSA9ICJ0aGlzIGlzIGEgc3RhcnRmbG93IGZyb20gdGFzay1leGVjdXRpb24tdGVzdC13b3JrZmxvdyIKICAgICAgICB9OwogICAgICAgIHN0YXJ0VGFzay5TZXRCb2R5KGJvZHkpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU3RhcnRGbG93TWFwcGluZyBJbnB1dEhhbmRsZXI6IGJvZHkgc2V0IHdpdGggcGFyZW50SW5zdGFuY2VJZC9zb3VyY2Uvbm90ZSIpOwogICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UoKTsKICAgIH0KCiAgICBwdWJsaWMgYXN5bmMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgdmFyIHRhc2tSZXNwb25zZSA9IGNvbnRleHQuQm9keTsKICAgICAgICB2YXIgcmVzcG9uc2VCb2R5ID0gdGFza1Jlc3BvbnNlLmRhdGE7CgogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwVGFza0NvbXBsZXRlZCIpKSByZXN1bHQuaHR0cFRhc2tDb21wbGV0ZWQgPSBkYXRhLmh0dHBUYXNrQ29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicHJvY2Vzc0lkIikpIHJlc3VsdC5wcm9jZXNzSWQgPSBkYXRhLnByb2Nlc3NJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInNjcmlwdFByb2Nlc3NlZCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkID0gZGF0YS5zY3JpcHRQcm9jZXNzZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcm9zc1dvcmtmbG93Q29tcGxldGVkIikpIHJlc3VsdC5jcm9zc1dvcmtmbG93Q29tcGxldGVkID0gZGF0YS5jcm9zc1dvcmtmbG93Q29tcGxldGVkOwoKICAgICAgICByZXN1bHQuc3RhcnRGbG93Q29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gdGFza1Jlc3BvbnNlLmlzU3VjY2VzczsKCiAgICAgICAgaWYgKHJlc3BvbnNlQm9keSAhPSBudWxsKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgImlkIikpCiAgICAgICAgICAgICAgICByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSByZXNwb25zZUJvZHkuaWQ7CiAgICAgICAgICAgIGVsc2UgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgImluc3RhbmNlSWQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5zdGFydGVkSW5zdGFuY2VJZCA9IHJlc3BvbnNlQm9keS5pbnN0YW5jZUlkOwogICAgICAgIH0KCiAgICAgICAgTG9nSW5mb3JtYXRpb24oJCJTdGFydEZsb3dNYXBwaW5nIE91dHB1dEhhbmRsZXI6IGlzU3VjY2Vzcz17dGFza1Jlc3BvbnNlLmlzU3VjY2Vzc30iKTsKICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9OwogICAgfQp9Cg==" } } ], @@ -494,7 +494,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/SubProcessMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFN1YlByb2Nlc3NNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRBdCIpKSByZXN1bHQuc3RhcnRlZEF0ID0gZGF0YS5zdGFydGVkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwVGFza0NvbXBsZXRlZCIpKSByZXN1bHQuaHR0cFRhc2tDb21wbGV0ZWQgPSBkYXRhLmh0dHBUYXNrQ29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFN0YXR1c0NvZGUiKSkgcmVzdWx0Lmh0dHBTdGF0dXNDb2RlID0gZGF0YS5odHRwU3RhdHVzQ29kZTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBJc1N1Y2Nlc3MiKSkgcmVzdWx0Lmh0dHBJc1N1Y2Nlc3MgPSBkYXRhLmh0dHBJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJwcm9jZXNzSWQiKSkgcmVzdWx0LnByb2Nlc3NJZCA9IGRhdGEucHJvY2Vzc0lkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkIikpIHJlc3VsdC5zY3JpcHRQcm9jZXNzZWQgPSBkYXRhLnNjcmlwdFByb2Nlc3NlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInNjcmlwdFByb2Nlc3NlZEF0IikpIHJlc3VsdC5zY3JpcHRQcm9jZXNzZWRBdCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcm9zc1dvcmtmbG93Q29tcGxldGVkIikpIHJlc3VsdC5jcm9zc1dvcmtmbG93Q29tcGxldGVkID0gZGF0YS5jcm9zc1dvcmtmbG93Q29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3Jvc3NXb3JrZmxvd0F0IikpIHJlc3VsdC5jcm9zc1dvcmtmbG93QXQgPSBkYXRhLmNyb3NzV29ya2Zsb3dBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0NvbXBsZXRlZCIpKSByZXN1bHQuc3RhcnRGbG93Q29tcGxldGVkID0gZGF0YS5zdGFydEZsb3dDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydEZsb3dJc1N1Y2Nlc3MiKSkgcmVzdWx0LnN0YXJ0Rmxvd0lzU3VjY2VzcyA9IGRhdGEuc3RhcnRGbG93SXNTdWNjZXNzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEluc3RhbmNlSWQiKSkgcmVzdWx0LnN0YXJ0ZWRJbnN0YW5jZUlkID0gZGF0YS5zdGFydGVkSW5zdGFuY2VJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUNvbXBsZXRlZCIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkID0gZGF0YS5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJnZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3MiKSkgcmVzdWx0LmdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyA9IGRhdGEuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicmVtb3RlSW5zdGFuY2VEYXRhIikpIHJlc3VsdC5yZW1vdGVJbnN0YW5jZURhdGEgPSBkYXRhLnJlbW90ZUluc3RhbmNlRGF0YTsKCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRhc2tSZXN1bHRzIikpCiAgICAgICAgewogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgIm5vdGlmaWNhdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uID0gZGF0YS50YXNrUmVzdWx0cy5ub3RpZmljYXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAidHJpZ2dlclRyYW5zaXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uID0gZGF0YS50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJzdWJwcm9jZXNzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzID0gZGF0YS50YXNrUmVzdWx0cy5zdWJwcm9jZXNzOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImdldEluc3RhbmNlcyIpKSByZXN1bHQudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzID0gZGF0YS50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXM7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiaHVtYW5UYXNrIikpIHJlc3VsdC50YXNrUmVzdWx0cy5odW1hblRhc2sgPSBkYXRhLnRhc2tSZXN1bHRzLmh1bWFuVGFzazsKICAgICAgICB9CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2Vzcy5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzLmV4ZWN1dGVkQXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlN1YlByb2Nlc3NNYXBwaW5nIGNvbXBsZXRlZCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBTdWJQcm9jZXNzTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICAvLyBTdWJQcm9jZXNzVGFzayAodGlwIDE0KSBmaXJlLWFuZC1mb3JnZXQgYmlyIGFsdCBpbnN0YW5jZSBhY2FyLgogICAgICAgIC8vIEJvZHkgdXplcmluZGVuIHN1YnByb2Nlc3MnaW4gaW5zdGFuY2UgZGF0YSdzaW5hIHBhcmVudCByZWZlcmFuc2kgdmUga2F5bmFrIG5vdHUgeWF6aXlvcnV6OwogICAgICAgIC8vIHRlc3RsZXIgYnUgYWxhbmxhcmkgc3VicHJvY2VzcyBHZXRJbnN0YW5jZSBhdHRyaWJ1dGVzJ3VuZGEgZG9ncnVsYXlhYmlsc2luLgogICAgICAgIHZhciBzdWJQcm9jZXNzVGFzayA9ICh0YXNrIGFzIFN1YlByb2Nlc3NUYXNrKSE7CgogICAgICAgIHZhciBib2R5ID0gbmV3CiAgICAgICAgewogICAgICAgICAgICBzb3VyY2UgPSAidGFzay1leGVjdXRpb24tdGVzdCIsCiAgICAgICAgICAgIHBhcmVudEluc3RhbmNlSWQgPSBjb250ZXh0Lkluc3RhbmNlLklkLAogICAgICAgICAgICBub3RlID0gInRoaXMgaXMgYSBzdWJwcm9jZXNzIHN0YXJ0ZWQgZnJvbSB0YXNrLWV4ZWN1dGlvbi10ZXN0LXdvcmtmbG93IgogICAgICAgIH07CiAgICAgICAgc3ViUHJvY2Vzc1Rhc2suU2V0Qm9keShib2R5KTsKCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlN1YlByb2Nlc3NNYXBwaW5nIElucHV0SGFuZGxlcjogYm9keSBzZXQgd2l0aCBwYXJlbnRJbnN0YW5jZUlkL3NvdXJjZS9ub3RlIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIHZhciB0YXNrUmVzcG9uc2UgPSBjb250ZXh0LkJvZHk7CiAgICAgICAgdmFyIHJlc3BvbnNlQm9keSA9IHRhc2tSZXNwb25zZT8uZGF0YTsKCiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkQXQiKSkgcmVzdWx0LnN0YXJ0ZWRBdCA9IGRhdGEuc3RhcnRlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBTdGF0dXNDb2RlIikpIHJlc3VsdC5odHRwU3RhdHVzQ29kZSA9IGRhdGEuaHR0cFN0YXR1c0NvZGU7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwSXNTdWNjZXNzIikpIHJlc3VsdC5odHRwSXNTdWNjZXNzID0gZGF0YS5odHRwSXNTdWNjZXNzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicHJvY2Vzc0lkIikpIHJlc3VsdC5wcm9jZXNzSWQgPSBkYXRhLnByb2Nlc3NJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInNjcmlwdFByb2Nlc3NlZCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkID0gZGF0YS5zY3JpcHRQcm9jZXNzZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWRBdCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkQXQgPSBkYXRhLnNjcmlwdFByb2Nlc3NlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCIpKSByZXN1bHQuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyb3NzV29ya2Zsb3dBdCIpKSByZXN1bHQuY3Jvc3NXb3JrZmxvd0F0ID0gZGF0YS5jcm9zc1dvcmtmbG93QXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydEZsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LnN0YXJ0Rmxvd0NvbXBsZXRlZCA9IGRhdGEuc3RhcnRGbG93Q29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93SXNTdWNjZXNzIikpIHJlc3VsdC5zdGFydEZsb3dJc1N1Y2Nlc3MgPSBkYXRhLnN0YXJ0Rmxvd0lzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRJbnN0YW5jZUlkIikpIHJlc3VsdC5zdGFydGVkSW5zdGFuY2VJZCA9IGRhdGEuc3RhcnRlZEluc3RhbmNlSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJnZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQiKSkgcmVzdWx0LmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZCA9IGRhdGEuZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3MgPSBkYXRhLmdldEluc3RhbmNlRGF0YUlzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInJlbW90ZUluc3RhbmNlRGF0YSIpKSByZXN1bHQucmVtb3RlSW5zdGFuY2VEYXRhID0gZGF0YS5yZW1vdGVJbnN0YW5jZURhdGE7CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0YXNrUmVzdWx0cyIpKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInRyaWdnZXJUcmFuc2l0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAic3VicHJvY2VzcyIpKSByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2VzcyA9IGRhdGEudGFza1Jlc3VsdHMuc3VicHJvY2VzczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJnZXRJbnN0YW5jZXMiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IGRhdGEudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImh1bWFuVGFzayIpKSByZXN1bHQudGFza1Jlc3VsdHMuaHVtYW5UYXNrID0gZGF0YS50YXNrUmVzdWx0cy5odW1hblRhc2s7CiAgICAgICAgfQoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2VzcyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MuY29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2Vzcy5leGVjdXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIC8vIFN1YlByb2Nlc3MgeWFuaXRpIHJ1bnRpbWUgZG9rdW1hbmluYSBnb3JlIGRhdGEuaWQgKHllbmkgaW5zdGFuY2UgaWQpIGRvbmVyLgogICAgICAgIC8vIEVza2kgeWFuaXRsYXJkYSBkYXRhLmluc3RhbmNlSWQgZ29ydWxlYmlsaXI7IGlraXNpbmkgZGUgZ3V2ZW5saSBzZWtpbGRlIGRlbmUuCiAgICAgICAgaWYgKHJlc3BvbnNlQm9keSAhPSBudWxsKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgImlkIikpCiAgICAgICAgICAgICAgICByZXN1bHQuc3VicHJvY2Vzc0luc3RhbmNlSWQgPSByZXNwb25zZUJvZHkuaWQ7CiAgICAgICAgICAgIGVsc2UgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgImluc3RhbmNlSWQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5zdWJwcm9jZXNzSW5zdGFuY2VJZCA9IHJlc3BvbnNlQm9keS5pbnN0YW5jZUlkOwogICAgICAgIH0KCiAgICAgICAgaWYgKHRhc2tSZXNwb25zZSAhPSBudWxsICYmIEhhc1Byb3BlcnR5KHRhc2tSZXNwb25zZSwgImlzU3VjY2VzcyIpKQogICAgICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2Vzcy5pc1N1Y2Nlc3MgPSB0YXNrUmVzcG9uc2UuaXNTdWNjZXNzOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU3ViUHJvY2Vzc01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } ], @@ -656,4 +656,4 @@ } ] } -} +} \ No newline at end of file diff --git a/doc/integration-test-documentation.md b/doc/integration-test-documentation.md index 4848e9c..6d9c9d7 100644 --- a/doc/integration-test-documentation.md +++ b/doc/integration-test-documentation.md @@ -414,11 +414,11 @@ extended-tasks-test-workflow (F) |---------|-------------------|---------------| | HTTP Task (type:6) | Mocklab HTTP | http-process-task + HttpProcessMapping | | Script Task (type:7) | C# script data | task-exec-script-task + ScriptProcessMapping | -| StartFlow Task (type:11) | Hedef workflow baslatma | start-flow-task + StartFlowMapping | +| StartFlow Task (type:11) | Hedef workflow baslatma + hedef instance teyidi (`startedInstanceId` + GET state) | start-flow-task + StartFlowMapping (SetBody parentInstanceId/source/note) | | GetInstanceData Task (type:13) | Baska instance datasi | get-instance-data-task + GetInstanceDataMapping | | Notification Task (type:10) | Platform mapping G (script yok) | notification-task + mapping `{ "type": "G" }` | | TriggerTransition Task (type:12) | Hedef instance + manuel gecis | trigger-transition-task + TriggerTransitionMapping (SetInstance + `manual-complete-target`) | -| SubProcess Task (type:14) | Alt surec baslatma | subprocess-task + SubProcessMapping | +| SubProcess Task (type:14) | Alt surec baslatma + hedef instance teyidi (`subprocessInstanceId` + GET state/attributes) | subprocess-task + SubProcessMapping (SetBody parentInstanceId/source/note) | | GetInstances Task (type:15) | Instance listesi | get-instances-task + GetInstancesMapping | | Human Task (type:5) | Manuel `approve-human-task` | human-task + HumanTaskMapping | | Dapr HTTP (1) / Service (3) / Binding (2) / PubSub (4) | Sidecar + YAML | `extended-tasks-test-workflow` + dapr-*-task | diff --git a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs index 37cbacb..32d8d5d 100644 --- a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs +++ b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs @@ -65,6 +65,13 @@ public static void AssertWhileWaitingOnHumanTask(JsonElement attributes) "completed", ContractHint ); + // SubProcessTask (tip 14) gercekten yeni bir hedef instance acmali; SubProcessMapping + // OutputHandler'i runtime yanitindaki id alanini parent attributes.subprocessInstanceId'ye yazar. + JsonElementAssertions.AssertPropertyNonEmptyString( + attributes, + "subprocessInstanceId", + ContractHint + ); JsonElementAssertions.AssertNestedPropertyTrue( attributes, new[] { "taskResults", "getInstances" }, diff --git a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs index 1ad0365..7abcdc1 100644 --- a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs +++ b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs @@ -79,6 +79,54 @@ await _mainWorkflow.WaitForStateAsync( ); Assert.Equal("C", StateFunctionJson.ExtractStatus(targetStateAfterTrigger)); + // SubProcessTask (tip 14) ayri bir hedef instance acar (fire-and-forget). + // attributes.subprocessInstanceId, SubProcessMapping OutputHandler'inda runtime yanitindan yazilir; + // varligi ve hedef workflow GET functions/state cevabinin Active/Completed olmasi, subprocess'in + // gercekten baslatildigini kanitlar (sadece "completed" bayragi yetmez). Ayrica GetInstance ile + // hedef instance attributes'inda parentInstanceId/source/note alanlarinin yazildigini dogrulariz. + var subprocessInstanceId = attrsBefore.GetProperty("subprocessInstanceId").GetString(); + Assert.False( + string.IsNullOrWhiteSpace(subprocessInstanceId), + "subprocessInstanceId should be a non-empty string from SubProcessMapping output" + ); + + var subprocessStateBody = await _targetWorkflow.GetStateFunctionBodyAsync( + subprocessInstanceId!, + headers: null + ); + // Subprocess fire-and-forget oldugu icin tetiklemiyoruz; ya target-initial'da Active kalmali + // ya da runtime tarafindan otomatik Completed olmus olmali. Her iki durumda da instance'in + // gercekten yaratildigi GET 200 + gecerli state ile teyit edilir. + var subprocessState = StateFunctionJson.ExtractStateName(subprocessStateBody); + var subprocessStatus = StateFunctionJson.ExtractStatus(subprocessStateBody); + Assert.True( + subprocessState == "target-initial" || subprocessState == "target-completed", + $"subprocess instance should be in target-initial or target-completed, got '{subprocessState}'" + ); + Assert.True( + subprocessStatus == "A" || subprocessStatus == "C", + $"subprocess instance status should be Active (A) or Completed (C), got '{subprocessStatus}'" + ); + + var subprocessAttrs = await GetAttributesAsync(TargetWorkflowKey, subprocessInstanceId!); + JsonElementAssertions.AssertPropertyString( + subprocessAttrs, + "source", + "task-execution-test", + "subprocess instance attributes.source (set via SubProcessTask body)" + ); + JsonElementAssertions.AssertPropertyString( + subprocessAttrs, + "parentInstanceId", + instanceId, + "subprocess instance attributes.parentInstanceId (must equal parent workflow instance id)" + ); + JsonElementAssertions.AssertPropertyNonEmptyString( + subprocessAttrs, + "note", + "subprocess instance attributes.note (set via SubProcessTask body)" + ); + await _mainWorkflow.RunTransitionAsync( instanceId, "approve-human-task", From dc4d805445e461a02d9fae15ed9e9f18874b833a Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Thu, 30 Apr 2026 16:53:39 +0300 Subject: [PATCH 09/24] =?UTF-8?q?cross-workflow-state=20was=20removed.=20T?= =?UTF-8?q?ask=20types=208=20(condition)=20and=209=20(timer)=20had=20been?= =?UTF-8?q?=20missing.=20For=20=E2=80=9Ccondition=E2=80=9D,=20supplying=20?= =?UTF-8?q?a=20rule=20was=20enough=20=E2=80=94=20there=20are=20already=20t?= =?UTF-8?q?ransitions=20that=20use=20rules.=20Nothing=20timer-related=20ex?= =?UTF-8?q?isted,=20so=20a=20transition=20that=20uses=20a=20timer=20was=20?= =?UTF-8?q?added.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lifecycle-transitions-test-workflow.http | 10 +- .../postman-lifecycle-transitions.json | 12 +- .../postman-task-execution-collection.json | 171 ++++++++++++++++-- api-tests/task-execution/task-execution.http | 11 +- core.link.json | 2 +- .../lifecycle-transitions-test-workflow.json | 6 +- .../src/ShortTimerMapping.csx | 4 +- .../task-execution-test-workflow.diagram.json | 2 +- .../CrossWorkflowMapping.csx | 26 --- .../GetInstanceDataMapping.csx | 3 +- .../GetInstancesMapping.csx | 4 +- .../HumanTaskMapping.csx | 4 +- .../StartFlowMapping.csx | 3 +- .../SubProcessMapping.csx | 17 +- .../TimerScheduleMapping.csx | 15 ++ .../TimerStartMapping.csx | 40 ++++ .../TriggerTransitionMapping.csx | 4 +- .../task-execution-test-workflow.json | 44 ++--- doc/integration-test-documentation.md | 32 ++-- .../LifecycleTransitionsTestWorkflowTests.cs | 10 +- .../TaskExecutionInstanceDataAssertions.cs | 28 ++- .../TaskExecutionTestWorkflowTests.cs | 19 ++ 22 files changed, 347 insertions(+), 120 deletions(-) delete mode 100644 core/Workflows/task-execution/src/task-execution-test-workflow/CrossWorkflowMapping.csx create mode 100644 core/Workflows/task-execution/src/task-execution-test-workflow/TimerScheduleMapping.csx create mode 100644 core/Workflows/task-execution/src/task-execution-test-workflow/TimerStartMapping.csx diff --git a/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http b/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http index 8747789..55c4a71 100644 --- a/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http +++ b/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http @@ -40,7 +40,7 @@ role: test-operator ### Adim 4: Durum Kontrol (auto-passed-state olmali, auto transition testPath=pass rule calisti) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{passInstanceId}}/functions/state -### Adim 5: 15 saniye bekle (scheduled timer 10s) ve durum kontrol +### Adim 5: ~12 saniye bekleyip durum kontrol (scheduled timer 6s + islem payi) # timer-triggered-state -> (auto AlwaysTrue) -> pre-complete-state olmali GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{passInstanceId}}/functions/state @@ -221,7 +221,7 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/ ### VNext.Testing.Sdk: StartInstanceAsync / RunTransitionAsync cagrilarinda ?sync=true (XML docs). ### Akis: Start -> move-to-processing (role test-operator) -> AssertState auto-passed-state ### -> cancel-schedule-manually (role header YOK) -> AssertState pre-complete-state -### Workflow: auto-passed-state icinde scheduled-timer 10s; iptal timer tetiklenmeden yapilir. +### Workflow: auto-passed-state icinde scheduled-timer 6s; iptal timer tetiklenmeden yapilir. ### ============================================================ ### Adim 1: StartInstanceAsync — key/tags/attributes C# ile ayni @@ -263,9 +263,9 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/ ### ============================================================ ### TEST: Reschedule Timer ($self + yeniden kurulan timer -> pre-complete) ### xUnit: RescheduleTimer_SelfTransition_ThenWaitsForRescheduledTimer_ReachesPreCompleteState -### Akis: move -> auto-passed -> reschedule-timer (yine auto-passed) -> ~10s sonra scheduled transition +### Akis: move -> auto-passed -> reschedule-timer (yine auto-passed) -> ~6s sonra scheduled transition ### -> timer-triggered-state -> auto-to-pre-complete -> pre-complete-state -### Adim 6 REST Client: reschedule sonrasi en az ~12-15 sn bekleyin (veya GET'i poll ile tekrarlayin). +### Adim 6 REST Client: reschedule sonrasi en az ~8-12 sn bekleyin (veya GET'i poll ile tekrarlayin). ### ============================================================ ### Adim 1: Workflow Baslat (Reschedule Test) @@ -304,7 +304,7 @@ Content-Type: application/json ### Adim 5: Durum Kontrol — hâlâ auto-passed-state (AssertStateAsync, $self) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rescheduleInstanceId}}/functions/state -### Adim 6: ~12-25 sn SONRA — pre-complete-state (WaitForStateAsync ile es; timer + timer-triggered auto) +### Adim 6: ~8-20 sn SONRA — pre-complete-state (WaitForStateAsync ile es; timer + timer-triggered auto) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rescheduleInstanceId}}/functions/state ############################################################################### diff --git a/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json b/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json index 563a268..33af4a8 100644 --- a/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json +++ b/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json @@ -192,7 +192,7 @@ "response": [] }, { - "name": "Adim 5: 15 saniye bekle (scheduled timer 10s) ve durum kontrol", + "name": "Adim 5: ~12 saniye bekleyip durum kontrol (scheduled timer 6s + islem payi)", "request": { "method": "GET", "header": [], @@ -992,7 +992,7 @@ }, { "name": "TEST: Schedule Cancel (Manual ile timer iptali)", - "description": "xUnit: ScheduleCancel_ManualTransition_CancelsTimerAndReachesPreComplete. VNext.Testing.Sdk tum start/transition cagrilarinda sync=true ekler. Akis: start -> move-to-processing (role: test-operator) -> state=auto-passed -> cancel-schedule-manually (rol header yok) -> state=pre-complete. Zamanlayici 10s; iptal bunun onunde yapilir.", + "description": "xUnit: ScheduleCancel_ManualTransition_CancelsTimerAndReachesPreComplete. VNext.Testing.Sdk tum start/transition cagrilarinda sync=true ekler. Akis: start -> move-to-processing (role: test-operator) -> state=auto-passed -> cancel-schedule-manually (rol header yok) -> state=pre-complete. Zamanlayici 6s; iptal bunun onunde yapilir.", "item": [ { "name": "Adim 1: Workflow Baslat (Schedule Cancel Test)", @@ -1278,7 +1278,7 @@ }, { "name": "TEST: Reschedule Timer ($self + yeniden kurulan timer -> pre-complete)", - "description": "xUnit: RescheduleTimer_SelfTransition_ThenWaitsForRescheduledTimer_ReachesPreCompleteState. reschedule-timer sonrasi auto-passed kali, sonra yeniden kurulan ~10s timer -> timer-triggered-state -> auto -> pre-complete. Adim 5b postman-echo 12s beklemesi icerir (offline ise Runner delay veya manuel bekleyip 5b atlayin; dogrudan Adim 6 GET ile poll edin).", + "description": "xUnit: RescheduleTimer_SelfTransition_ThenWaitsForRescheduledTimer_ReachesPreCompleteState. reschedule-timer sonrasi auto-passed kali, sonra yeniden kurulan ~6s timer -> timer-triggered-state -> auto -> pre-complete. Adim 5b postman-echo 8s beklemesi icerir (offline ise Runner delay veya manuel bekleyip 5b atlayin; dogrudan Adim 6 GET ile poll edin).", "item": [ { "name": "Adim 1: Workflow Baslat (Reschedule Test)", @@ -1542,12 +1542,12 @@ ] }, { - "name": "Adim 5b: Bekleme ~12s (yeniden kurulan timer; postman-echo — opsiyonel)", + "name": "Adim 5b: Bekleme ~8s (yeniden kurulan timer; postman-echo — opsiyonel)", "request": { "method": "GET", "header": [], "url": { - "raw": "https://postman-echo.com/delay/12", + "raw": "https://postman-echo.com/delay/8", "protocol": "https", "host": [ "postman-echo", @@ -1555,7 +1555,7 @@ ], "path": [ "delay", - "12" + "8" ] } }, diff --git a/api-tests/task-execution/postman-task-execution-collection.json b/api-tests/task-execution/postman-task-execution-collection.json index 181a017..0f15b1c 100644 --- a/api-tests/task-execution/postman-task-execution-collection.json +++ b/api-tests/task-execution/postman-task-execution-collection.json @@ -2,7 +2,7 @@ "info": { "_postman_id": "a1b2c3d4-e5f6-4123-a8cd-ef1234567890", "name": "vNext Test - Task execution (birlesik)", - "description": "task-execution-test-workflow, task-target-workflow, extended-tasks-test-workflow. Sadece happy path. Kaynak: api-tests/task-execution/task-execution.http", + "description": "task-execution-test-workflow, task-target-workflow, extended-tasks-test-workflow. Sadece happy path. Kaynak: api-tests/task-execution/task-execution.http.\n\n**Timer Task (tip 9):** Ana zincirde script-processing-state sonrasinda timer-wait-state bulunur. Bu state'in cikis transition'i `triggerType: 2` (scheduled) + `ITimerMapping` (TimerScheduleMapping.csx) ile 3 sn bekler. Adim 1 testleri parent attributes.timerStartedAt ile (Date.now() - ts) >= 2500 ms olarak Timer Task'in gercekten beklemis oldugunu dogrular. Condition Task (tip 8) tum auto transition rule'larinda (AlwaysTrueRule) dolayli olarak uretilir.\n\n**Child instance variable senkronu:** Adim 1 (sync start) ve Adim 3 (functions/data) Tests script'leri parent instance data'dan (kok veya attributes) collection variable doldurur: taskExecStartedTargetId, taskExecSubprocessId, taskExecSubflowInstanceId; istege bagli hedef workflow key: childWorkflowStartFlow, childWorkflowSubprocess, childWorkflowSubflow (varsayilan task-target-workflow). Adim 3a–3d zincirleri bu variable'larla StartFlow / SubProcess / Subflow hedeflerinin state, data ve instance GET isteklerini calistirir.\n\nB2 bagimsiz hedef workflow: targetWorkflow + targetInstanceId.", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "variable": [ @@ -12,7 +12,16 @@ { "key": "workflow", "value": "task-execution-test-workflow", "type": "string" }, { "key": "targetWorkflow", "value": "task-target-workflow", "type": "string" }, { "key": "extendedWorkflow", "value": "extended-tasks-test-workflow", "type": "string" }, - { "key": "mocklabBaseUrl", "value": "http://localhost:3002", "type": "string" } + { "key": "mocklabBaseUrl", "value": "http://localhost:3002", "type": "string" }, + { "key": "taskExecInstanceId", "value": "", "type": "string" }, + { "key": "taskExecStartedTargetId", "value": "", "type": "string" }, + { "key": "taskExecSubprocessId", "value": "", "type": "string" }, + { "key": "taskExecSubflowInstanceId", "value": "", "type": "string" }, + { "key": "childWorkflowStartFlow", "value": "task-target-workflow", "type": "string" }, + { "key": "childWorkflowSubprocess", "value": "task-target-workflow", "type": "string" }, + { "key": "childWorkflowSubflow", "value": "task-target-workflow", "type": "string" }, + { "key": "targetInstanceId", "value": "", "type": "string" }, + { "key": "extendedInstanceId", "value": "", "type": "string" } ], "item": [ { @@ -42,10 +51,43 @@ "script": { "type": "text/javascript", "exec": [ - "var jsonData = pm.response.json();", - "if (jsonData && jsonData.id) {", + "pm.test(\"Adim 1: collection variables — sync start (child id + workflow key)\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData, \"start response\").to.be.ok;", + " pm.expect(jsonData.id, \"parent instance id\").to.be.a(\"string\").and.not.empty;", " pm.collectionVariables.set(\"taskExecInstanceId\", jsonData.id);", - "}" + " var a = jsonData.attributes || {};", + " function optPick(root, attr, keys) {", + " for (var i = 0; i < keys.length; i++) {", + " var k = keys[i];", + " var v = root[k] !== undefined && root[k] !== null ? root[k] : attr[k];", + " if (v !== undefined && v !== null && String(v).length) return String(v);", + " }", + " return \"\";", + " }", + " var started = optPick(jsonData, a, [\"startedInstanceId\"]);", + " var sub = optPick(jsonData, a, [\"subprocessInstanceId\"]);", + " pm.expect(started, \"startedInstanceId (StartFlow child)\").to.be.a(\"string\").and.not.empty;", + " pm.expect(sub, \"subprocessInstanceId (SubProcess child)\").to.be.a(\"string\").and.not.empty;", + " pm.collectionVariables.set(\"taskExecStartedTargetId\", started);", + " pm.collectionVariables.set(\"taskExecSubprocessId\", sub);", + " var timerStartedAt = optPick(jsonData, a, [\"timerStartedAt\"]);", + " pm.expect(timerStartedAt, \"timerStartedAt (Timer Task tip 9 - timer-wait-state)\").to.be.a(\"string\").and.not.empty;", + " var ts = Date.parse(timerStartedAt);", + " pm.expect(isNaN(ts), \"timerStartedAt should be parseable ISO 8601\").to.equal(false);", + " var elapsedMs = Date.now() - ts;", + " pm.expect(elapsedMs, \"sync start oncesinde human-task-state'e ulasilmasi 3 sn'lik scheduled transition'i ortaya cikarmali (>= 2500 ms)\").to.be.at.least(2500);", + " var expectedSeconds = (a && typeof a.timerExpectedSeconds === \"number\") ? a.timerExpectedSeconds : (typeof jsonData.timerExpectedSeconds === \"number\" ? jsonData.timerExpectedSeconds : null);", + " pm.expect(expectedSeconds, \"timerExpectedSeconds attribute (TimerStartMapping)\").to.equal(3);", + " var sfId = optPick(jsonData, a, [\"subflowInstanceId\", \"subFlowInstanceId\", \"childSubFlowInstanceId\"]);", + " if (sfId) pm.collectionVariables.set(\"taskExecSubflowInstanceId\", sfId);", + " var wf1 = optPick(jsonData, a, [\"startFlowTargetWorkflow\", \"startedTargetWorkflowKey\", \"childWorkflowStartFlow\"]);", + " if (wf1) pm.collectionVariables.set(\"childWorkflowStartFlow\", wf1);", + " var wf2 = optPick(jsonData, a, [\"subprocessTargetWorkflow\", \"subprocessWorkflowKey\", \"childWorkflowSubprocess\"]);", + " if (wf2) pm.collectionVariables.set(\"childWorkflowSubprocess\", wf2);", + " var wf3 = optPick(jsonData, a, [\"subflowTargetWorkflow\", \"subflowWorkflowKey\", \"childWorkflowSubflow\"]);", + " if (wf3) pm.collectionVariables.set(\"childWorkflowSubflow\", wf3);", + "});" ] } } @@ -82,13 +124,29 @@ "script": { "type": "text/javascript", "exec": [ - "var jsonData = pm.response.json();", - "if (jsonData && jsonData.startedInstanceId) {", - " pm.collectionVariables.set(\"taskExecStartedTargetId\", jsonData.startedInstanceId);", + "function __attr(p) { return p && p.attributes && typeof p.attributes === \"object\" ? p.attributes : {}; }", + "function __pick(p, keys) {", + " var a = __attr(p);", + " for (var i = 0; i < keys.length; i++) {", + " var k = keys[i];", + " var v = p[k] !== undefined && p[k] !== null ? p[k] : a[k];", + " if (v !== undefined && v !== null && String(v).length) return String(v);", + " }", + " return \"\";", "}", - "if (jsonData && jsonData.subprocessInstanceId) {", - " pm.collectionVariables.set(\"taskExecSubprocessId\", jsonData.subprocessInstanceId);", - "}" + "var j = pm.response.json();", + "var s1 = __pick(j, [\"startedInstanceId\"]);", + "var s2 = __pick(j, [\"subprocessInstanceId\"]);", + "var s3 = __pick(j, [\"subflowInstanceId\", \"subFlowInstanceId\", \"childSubFlowInstanceId\"]);", + "var w1 = __pick(j, [\"startFlowTargetWorkflow\", \"startedTargetWorkflowKey\", \"childWorkflowStartFlow\"]);", + "var w2 = __pick(j, [\"subprocessTargetWorkflow\", \"subprocessWorkflowKey\", \"childWorkflowSubprocess\"]);", + "var w3 = __pick(j, [\"subflowTargetWorkflow\", \"subflowWorkflowKey\", \"childWorkflowSubflow\"]);", + "if (s1) pm.collectionVariables.set(\"taskExecStartedTargetId\", s1);", + "if (s2) pm.collectionVariables.set(\"taskExecSubprocessId\", s2);", + "if (s3) pm.collectionVariables.set(\"taskExecSubflowInstanceId\", s3);", + "if (w1) pm.collectionVariables.set(\"childWorkflowStartFlow\", w1);", + "if (w2) pm.collectionVariables.set(\"childWorkflowSubprocess\", w2);", + "if (w3) pm.collectionVariables.set(\"childWorkflowSubflow\", w3);" ] } } @@ -100,9 +158,35 @@ "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{taskExecStartedTargetId}}/functions/state", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflowStartFlow}}/instances/{{taskExecStartedTargetId}}/functions/state", "host": ["{{baseUrl}}"], - "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{targetWorkflow}}", "instances", "{{taskExecStartedTargetId}}", "functions", "state"] + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{childWorkflowStartFlow}}", "instances", "{{taskExecStartedTargetId}}", "functions", "state"] + } + }, + "response": [] + }, + { + "name": "Adim 3a-2: StartFlow hedef — functions/data", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflowStartFlow}}/instances/{{taskExecStartedTargetId}}/functions/data", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{childWorkflowStartFlow}}", "instances", "{{taskExecStartedTargetId}}", "functions", "data"] + } + }, + "response": [] + }, + { + "name": "Adim 3a-3: StartFlow hedef — instance (attributes: source, parentInstanceId, note)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflowStartFlow}}/instances/{{taskExecStartedTargetId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{childWorkflowStartFlow}}", "instances", "{{taskExecStartedTargetId}}"] } }, "response": [] @@ -113,9 +197,22 @@ "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{taskExecSubprocessId}}/functions/state", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflowSubprocess}}/instances/{{taskExecSubprocessId}}/functions/state", "host": ["{{baseUrl}}"], - "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{targetWorkflow}}", "instances", "{{taskExecSubprocessId}}", "functions", "state"] + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{childWorkflowSubprocess}}", "instances", "{{taskExecSubprocessId}}", "functions", "state"] + } + }, + "response": [] + }, + { + "name": "Adim 3b-2: SubProcess hedef — functions/data", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflowSubprocess}}/instances/{{taskExecSubprocessId}}/functions/data", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{childWorkflowSubprocess}}", "instances", "{{taskExecSubprocessId}}", "functions", "data"] } }, "response": [] @@ -126,9 +223,49 @@ "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{taskExecSubprocessId}}", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflowSubprocess}}/instances/{{taskExecSubprocessId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{childWorkflowSubprocess}}", "instances", "{{taskExecSubprocessId}}"] + } + }, + "response": [] + }, + { + "name": "Adim 3d: Subflow hedef — functions/state (parent data'da subflowInstanceId vb. ise)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflowSubflow}}/instances/{{taskExecSubflowInstanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{childWorkflowSubflow}}", "instances", "{{taskExecSubflowInstanceId}}", "functions", "state"] + }, + "description": "Oncelik: taskExecSubflowInstanceId dolu olmali (Adim 1/3 senkronu: subflowInstanceId, subFlowInstanceId, childSubFlowInstanceId). Bos id ile URL gecersiz olur — subflow yoksa atla." + }, + "response": [] + }, + { + "name": "Adim 3d-2: Subflow hedef — functions/data", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflowSubflow}}/instances/{{taskExecSubflowInstanceId}}/functions/data", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{childWorkflowSubflow}}", "instances", "{{taskExecSubflowInstanceId}}", "functions", "data"] + } + }, + "response": [] + }, + { + "name": "Adim 3d-3: Subflow hedef — instance (GET)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflowSubflow}}/instances/{{taskExecSubflowInstanceId}}", "host": ["{{baseUrl}}"], - "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{targetWorkflow}}", "instances", "{{taskExecSubprocessId}}"] + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{childWorkflowSubflow}}", "instances", "{{taskExecSubflowInstanceId}}"] } }, "response": [] diff --git a/api-tests/task-execution/task-execution.http b/api-tests/task-execution/task-execution.http index 44e72eb..5d3c6ac 100644 --- a/api-tests/task-execution/task-execution.http +++ b/api-tests/task-execution/task-execution.http @@ -16,9 +16,11 @@ ############################################################################### # BOLUM 1 — Happy path: Ana task execution zinciri -# HTTP -> Script -> Cross workflow -> Start Flow (11) -> Get Instance Data (13) +# HTTP -> Script -> Timer Wait (Timer Task tip 9, scheduled transition 3 sn) +# -> Start Flow (11) -> Get Instance Data (13) # -> Notification (10, mapping.type G) -> TriggerTransition (12) -> SubProcess (14) # -> GetInstances (15) -> Human Task (5) -> PATCH approve -> Completed +# Condition Task (tip 8) tum auto transition rule'larinda (AlwaysTrueRule) dolayli olarak uretilir. # Requires: MocklabBaseUrl config, Mocklab seeded (POST /api/test/process) ############################################################################### @@ -39,12 +41,17 @@ Content-Type: application/json GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/state ### B1 — Adim 3: Instance data (notification / trigger / subprocess / getInstances bayraklari) -# Beklenen: testId, httpTaskCompleted, scriptProcessed, crossWorkflowCompleted, +# Beklenen: testId, httpTaskCompleted, scriptProcessed, +# timerStartedAt (TimerStartMapping anligi - timer-wait-state'e girildigi an), +# timerExpectedSeconds = 3 (TimerScheduleMapping bekleme suresi), # startFlowCompleted, startedInstanceId (StartTask sonucu hedef workflow id), # subprocessInstanceId (SubProcessTask sonucu hedef workflow id), +# subprocessData (SubProcessTask context.Body.data anlık kopyası: id/instanceId/state vb.), # getInstanceDataCompleted, taskResults.triggerTransition.completed, # taskResults.subprocess.completed, taskResults.getInstances.completed, # taskResults.humanTask.completed +# Not: human-task-state'e ulasilana kadar gecen toplam sure >= 3 sn olmali; aksi halde +# scheduled transition'in Timer Task'i devre disi olabilir. GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/data ### B1 — Adim 3a: StartTask ile baslatilan hedef instance functions/state (target-completed olmali) diff --git a/core.link.json b/core.link.json index 10dd625..35c50c1 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-04-30T12:27:03.890Z" + "importedAt": "2026-04-30T13:51:28.126Z" } \ No newline at end of file diff --git a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json index 12d0f2b..5e00605 100644 --- a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json +++ b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json @@ -330,16 +330,16 @@ "labels": [ { "language": "en-US", - "label": "Scheduled Timer (ITimerMapping, 10s)" + "label": "Scheduled Timer (ITimerMapping, 6s)" }, { "language": "tr-TR", - "label": "Zamanlanmis Gecis (ITimerMapping, 10s)" + "label": "Zamanlanmis Gecis (ITimerMapping, 6s)" } ], "timer": { "location": "./src/ShortTimerMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFNob3J0VGltZXJNYXBwaW5nIDogU2NyaXB0QmFzZSwgSVRpbWVyTWFwcGluZwp7CiAgICBwdWJsaWMgYXN5bmMgVGFzazxUaW1lclNjaGVkdWxlPiBIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU2hvcnRUaW1lck1hcHBpbmc6IHNjaGVkdWxpbmcgMTAgc2Vjb25kcyBmcm9tIG5vdyIpOwogICAgICAgIHJldHVybiBUaW1lclNjaGVkdWxlLkZyb21EYXRlVGltZShEYXRlVGltZS5VdGNOb3cuQWRkU2Vjb25kcygxMCkpOwogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFNob3J0VGltZXJNYXBwaW5nIDogU2NyaXB0QmFzZSwgSVRpbWVyTWFwcGluZwp7CiAgICBwdWJsaWMgYXN5bmMgVGFzazxUaW1lclNjaGVkdWxlPiBIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU2hvcnRUaW1lck1hcHBpbmc6IHNjaGVkdWxpbmcgNiBzZWNvbmRzIGZyb20gbm93Iik7CiAgICAgICAgcmV0dXJuIFRpbWVyU2NoZWR1bGUuRnJvbURhdGVUaW1lKERhdGVUaW1lLlV0Y05vdy5BZGRTZWNvbmRzKDYpKTsKICAgIH0KfQo=" }, "onExecutionTasks": [] }, diff --git a/core/Workflows/lifecycle-transitions/src/ShortTimerMapping.csx b/core/Workflows/lifecycle-transitions/src/ShortTimerMapping.csx index 5021443..7f529ad 100644 --- a/core/Workflows/lifecycle-transitions/src/ShortTimerMapping.csx +++ b/core/Workflows/lifecycle-transitions/src/ShortTimerMapping.csx @@ -6,7 +6,7 @@ public class ShortTimerMapping : ScriptBase, ITimerMapping { public async Task Handler(ScriptContext context) { - LogInformation("ShortTimerMapping: scheduling 10 seconds from now"); - return TimerSchedule.FromDateTime(DateTime.UtcNow.AddSeconds(10)); + LogInformation("ShortTimerMapping: scheduling 6 seconds from now"); + return TimerSchedule.FromDateTime(DateTime.UtcNow.AddSeconds(6)); } } diff --git a/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json b/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json index 206dc46..add8d23 100644 --- a/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json +++ b/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json @@ -8,7 +8,7 @@ "x": 950, "y": 20 }, - "cross-workflow-state": { + "timer-wait-state": { "x": 1220, "y": 320 }, diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/CrossWorkflowMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/CrossWorkflowMapping.csx deleted file mode 100644 index ea994ee..0000000 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/CrossWorkflowMapping.csx +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Dynamic; -using System.Threading.Tasks; -using BBT.Workflow.Scripting; -using BBT.Workflow.Definitions; - -public class CrossWorkflowMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public Task OutputHandler(ScriptContext context) - { - var data = context.Instance.Data; - dynamic result = new ExpandoObject(); - if (HasProperty(data, "testId")) result.testId = data.testId; - if (HasProperty(data, "httpTaskCompleted")) result.httpTaskCompleted = data.httpTaskCompleted; - if (HasProperty(data, "scriptProcessed")) result.scriptProcessed = data.scriptProcessed; - result.crossWorkflowCompleted = true; - result.crossWorkflowAt = DateTime.UtcNow.ToString("o"); - LogInformation("CrossWorkflowMapping completed"); - return Task.FromResult(new ScriptResponse { Data = result }); - } -} diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstanceDataMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstanceDataMapping.csx index afbfe35..e06554e 100644 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstanceDataMapping.csx +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstanceDataMapping.csx @@ -33,7 +33,8 @@ public class GetInstanceDataMapping : ScriptBase, IMapping if (HasProperty(data, "httpTaskCompleted")) result.httpTaskCompleted = data.httpTaskCompleted; if (HasProperty(data, "processId")) result.processId = data.processId; if (HasProperty(data, "scriptProcessed")) result.scriptProcessed = data.scriptProcessed; - if (HasProperty(data, "crossWorkflowCompleted")) result.crossWorkflowCompleted = data.crossWorkflowCompleted; + if (HasProperty(data, "timerStartedAt")) result.timerStartedAt = data.timerStartedAt; + if (HasProperty(data, "timerExpectedSeconds")) result.timerExpectedSeconds = data.timerExpectedSeconds; if (HasProperty(data, "startFlowCompleted")) result.startFlowCompleted = data.startFlowCompleted; if (HasProperty(data, "startedInstanceId")) result.startedInstanceId = data.startedInstanceId; diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstancesMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstancesMapping.csx index f6c2ce5..30bad3c 100644 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstancesMapping.csx +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstancesMapping.csx @@ -24,8 +24,8 @@ public class GetInstancesMapping : ScriptBase, IMapping if (HasProperty(data, "processId")) result.processId = data.processId; if (HasProperty(data, "scriptProcessed")) result.scriptProcessed = data.scriptProcessed; if (HasProperty(data, "scriptProcessedAt")) result.scriptProcessedAt = data.scriptProcessedAt; - if (HasProperty(data, "crossWorkflowCompleted")) result.crossWorkflowCompleted = data.crossWorkflowCompleted; - if (HasProperty(data, "crossWorkflowAt")) result.crossWorkflowAt = data.crossWorkflowAt; + if (HasProperty(data, "timerStartedAt")) result.timerStartedAt = data.timerStartedAt; + if (HasProperty(data, "timerExpectedSeconds")) result.timerExpectedSeconds = data.timerExpectedSeconds; if (HasProperty(data, "startFlowCompleted")) result.startFlowCompleted = data.startFlowCompleted; if (HasProperty(data, "startFlowIsSuccess")) result.startFlowIsSuccess = data.startFlowIsSuccess; if (HasProperty(data, "startedInstanceId")) result.startedInstanceId = data.startedInstanceId; diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/HumanTaskMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/HumanTaskMapping.csx index 42728fd..5a65b73 100644 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/HumanTaskMapping.csx +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/HumanTaskMapping.csx @@ -24,8 +24,8 @@ public class HumanTaskMapping : ScriptBase, IMapping if (HasProperty(data, "processId")) result.processId = data.processId; if (HasProperty(data, "scriptProcessed")) result.scriptProcessed = data.scriptProcessed; if (HasProperty(data, "scriptProcessedAt")) result.scriptProcessedAt = data.scriptProcessedAt; - if (HasProperty(data, "crossWorkflowCompleted")) result.crossWorkflowCompleted = data.crossWorkflowCompleted; - if (HasProperty(data, "crossWorkflowAt")) result.crossWorkflowAt = data.crossWorkflowAt; + if (HasProperty(data, "timerStartedAt")) result.timerStartedAt = data.timerStartedAt; + if (HasProperty(data, "timerExpectedSeconds")) result.timerExpectedSeconds = data.timerExpectedSeconds; if (HasProperty(data, "startFlowCompleted")) result.startFlowCompleted = data.startFlowCompleted; if (HasProperty(data, "startFlowIsSuccess")) result.startFlowIsSuccess = data.startFlowIsSuccess; if (HasProperty(data, "startedInstanceId")) result.startedInstanceId = data.startedInstanceId; diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/StartFlowMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/StartFlowMapping.csx index 175a4e3..18da156 100644 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/StartFlowMapping.csx +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/StartFlowMapping.csx @@ -37,7 +37,8 @@ public class StartFlowMapping : ScriptBase, IMapping if (HasProperty(data, "httpTaskCompleted")) result.httpTaskCompleted = data.httpTaskCompleted; if (HasProperty(data, "processId")) result.processId = data.processId; if (HasProperty(data, "scriptProcessed")) result.scriptProcessed = data.scriptProcessed; - if (HasProperty(data, "crossWorkflowCompleted")) result.crossWorkflowCompleted = data.crossWorkflowCompleted; + if (HasProperty(data, "timerStartedAt")) result.timerStartedAt = data.timerStartedAt; + if (HasProperty(data, "timerExpectedSeconds")) result.timerExpectedSeconds = data.timerExpectedSeconds; result.startFlowCompleted = true; result.startFlowIsSuccess = taskResponse.isSuccess; diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx index 2b6a3df..237b4b5 100644 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx @@ -42,8 +42,8 @@ public class SubProcessMapping : ScriptBase, IMapping if (HasProperty(data, "processId")) result.processId = data.processId; if (HasProperty(data, "scriptProcessed")) result.scriptProcessed = data.scriptProcessed; if (HasProperty(data, "scriptProcessedAt")) result.scriptProcessedAt = data.scriptProcessedAt; - if (HasProperty(data, "crossWorkflowCompleted")) result.crossWorkflowCompleted = data.crossWorkflowCompleted; - if (HasProperty(data, "crossWorkflowAt")) result.crossWorkflowAt = data.crossWorkflowAt; + if (HasProperty(data, "timerStartedAt")) result.timerStartedAt = data.timerStartedAt; + if (HasProperty(data, "timerExpectedSeconds")) result.timerExpectedSeconds = data.timerExpectedSeconds; if (HasProperty(data, "startFlowCompleted")) result.startFlowCompleted = data.startFlowCompleted; if (HasProperty(data, "startFlowIsSuccess")) result.startFlowIsSuccess = data.startFlowIsSuccess; if (HasProperty(data, "startedInstanceId")) result.startedInstanceId = data.startedInstanceId; @@ -65,10 +65,19 @@ public class SubProcessMapping : ScriptBase, IMapping result.taskResults.subprocess.completed = true; result.taskResults.subprocess.executedAt = DateTime.UtcNow.ToString("o"); - // SubProcess yaniti runtime dokumanina gore data.id (yeni instance id) doner. - // Eski yanitlarda data.instanceId gorulebilir; ikisini de guvenli sekilde dene. + // SubProcessTask yanitinin gercek servis "data" govdesi parent attributes.subprocessData altinda saklanir + // (integration test ve operasyonel inceleme icin). Ayrica subprocessInstanceId kolay erisim icin ayri yazilir. if (responseBody != null) { + dynamic snapshot = new ExpandoObject(); + if (HasProperty(responseBody, "id")) snapshot.id = responseBody.id; + if (HasProperty(responseBody, "instanceId")) snapshot.instanceId = responseBody.instanceId; + if (HasProperty(responseBody, "state")) snapshot.state = responseBody.state; + if (HasProperty(responseBody, "launched")) snapshot.launched = responseBody.launched; + if (HasProperty(responseBody, "status")) snapshot.status = responseBody.status; + if (HasProperty(responseBody, "statusCode")) snapshot.statusCode = responseBody.statusCode; + result.subprocessData = snapshot; + if (HasProperty(responseBody, "id")) result.subprocessInstanceId = responseBody.id; else if (HasProperty(responseBody, "instanceId")) diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/TimerScheduleMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/TimerScheduleMapping.csx new file mode 100644 index 0000000..1bd1859 --- /dev/null +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/TimerScheduleMapping.csx @@ -0,0 +1,15 @@ +using System; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; + +public class TimerScheduleMapping : ScriptBase, ITimerMapping +{ + public Task Handler(ScriptContext context) + { + // Scheduled transition (triggerType: 2) icin Timer Task (tip 9) burada uretilir. + // 3 sn sonrasini dondurerek runtime'in beklemesini sagliyoruz. + var fireAt = DateTime.UtcNow.AddSeconds(3); + LogInformation($"TimerScheduleMapping: scheduling at {fireAt:o} (3 seconds from now)"); + return Task.FromResult(TimerSchedule.FromDateTime(fireAt)); + } +} diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/TimerStartMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/TimerStartMapping.csx new file mode 100644 index 0000000..dd3a612 --- /dev/null +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/TimerStartMapping.csx @@ -0,0 +1,40 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; +using BBT.Workflow.Definitions; + +public class TimerStartMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public Task OutputHandler(ScriptContext context) + { + var data = context.Instance.Data; + var now = DateTime.UtcNow; + + dynamic result = new ExpandoObject(); + + if (HasProperty(data, "testId")) result.testId = data.testId; + if (HasProperty(data, "startedAt")) result.startedAt = data.startedAt; + if (HasProperty(data, "httpTaskCompleted")) result.httpTaskCompleted = data.httpTaskCompleted; + if (HasProperty(data, "httpStatusCode")) result.httpStatusCode = data.httpStatusCode; + if (HasProperty(data, "httpIsSuccess")) result.httpIsSuccess = data.httpIsSuccess; + if (HasProperty(data, "processId")) result.processId = data.processId; + if (HasProperty(data, "scriptProcessed")) result.scriptProcessed = data.scriptProcessed; + if (HasProperty(data, "scriptProcessedAt")) result.scriptProcessedAt = data.scriptProcessedAt; + + // timerStartedAt: timer-wait-state'e girildigi an. Scheduled transition (Timer Task tip 9) + // 3 sn sonra start-flow-state'e gecirir; testler human-task-state'te attributes okuyup + // (DateTime.UtcNow - timerStartedAt) >= ~3 sn olmasini dogrular -> Timer Task gercekten beklemis demektir. + result.timerStartedAt = now.ToString("o"); + result.timerExpectedSeconds = 3; + result.timerCompleted = false; + + LogInformation($"TimerStartMapping: timerStartedAt={result.timerStartedAt}, timerExpectedSeconds=3"); + return Task.FromResult(new ScriptResponse { Data = result }); + } +} diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx index dd10dbc..abc85d0 100644 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx @@ -35,8 +35,8 @@ public class TriggerTransitionMapping : ScriptBase, IMapping if (HasProperty(data, "processId")) result.processId = data.processId; if (HasProperty(data, "scriptProcessed")) result.scriptProcessed = data.scriptProcessed; if (HasProperty(data, "scriptProcessedAt")) result.scriptProcessedAt = data.scriptProcessedAt; - if (HasProperty(data, "crossWorkflowCompleted")) result.crossWorkflowCompleted = data.crossWorkflowCompleted; - if (HasProperty(data, "crossWorkflowAt")) result.crossWorkflowAt = data.crossWorkflowAt; + if (HasProperty(data, "timerStartedAt")) result.timerStartedAt = data.timerStartedAt; + if (HasProperty(data, "timerExpectedSeconds")) result.timerExpectedSeconds = data.timerExpectedSeconds; if (HasProperty(data, "startFlowCompleted")) result.startFlowCompleted = data.startFlowCompleted; if (HasProperty(data, "startFlowIsSuccess")) result.startFlowIsSuccess = data.startFlowIsSuccess; if (HasProperty(data, "startedInstanceId")) result.startedInstanceId = data.startedInstanceId; diff --git a/core/Workflows/task-execution/task-execution-test-workflow.json b/core/Workflows/task-execution/task-execution-test-workflow.json index f916672..f82cc39 100644 --- a/core/Workflows/task-execution/task-execution-test-workflow.json +++ b/core/Workflows/task-execution/task-execution-test-workflow.json @@ -160,18 +160,18 @@ "onExits": [], "transitions": [ { - "key": "auto-to-cross-workflow", - "target": "cross-workflow-state", + "key": "auto-to-timer-wait", + "target": "timer-wait-state", "triggerType": 1, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Auto to Cross Workflow" + "label": "Auto to Timer Wait" }, { "language": "tr-TR", - "label": "Capraz Is Akisi Otomatik Gecis" + "label": "Zamanlayici Beklemeye Otomatik Gecis" } ], "rule": { @@ -183,18 +183,18 @@ ] }, { - "key": "cross-workflow-state", + "key": "timer-wait-state", "stateType": 2, "subType": 0, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Cross Workflow State" + "label": "Timer Wait State (Timer Task type 9)" }, { "language": "tr-TR", - "label": "Capraz Is Akisi Durumu" + "label": "Zamanlayici Bekleme Durumu (Timer Task tip 9)" } ], "view": null, @@ -209,31 +209,31 @@ "flow": "sys-tasks" }, "mapping": { - "location": "./src/task-execution-test-workflow/CrossWorkflowMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIENyb3NzV29ya2Zsb3dNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInNjcmlwdFByb2Nlc3NlZCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkID0gZGF0YS5zY3JpcHRQcm9jZXNzZWQ7CiAgICAgICAgcmVzdWx0LmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC5jcm9zc1dvcmtmbG93QXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKICAgICAgICBMb2dJbmZvcm1hdGlvbigiQ3Jvc3NXb3JrZmxvd01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" + "location": "./src/task-execution-test-workflow/TimerStartMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFRpbWVyU3RhcnRNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICB2YXIgbm93ID0gRGF0ZVRpbWUuVXRjTm93OwoKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRBdCIpKSByZXN1bHQuc3RhcnRlZEF0ID0gZGF0YS5zdGFydGVkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwVGFza0NvbXBsZXRlZCIpKSByZXN1bHQuaHR0cFRhc2tDb21wbGV0ZWQgPSBkYXRhLmh0dHBUYXNrQ29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFN0YXR1c0NvZGUiKSkgcmVzdWx0Lmh0dHBTdGF0dXNDb2RlID0gZGF0YS5odHRwU3RhdHVzQ29kZTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBJc1N1Y2Nlc3MiKSkgcmVzdWx0Lmh0dHBJc1N1Y2Nlc3MgPSBkYXRhLmh0dHBJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJwcm9jZXNzSWQiKSkgcmVzdWx0LnByb2Nlc3NJZCA9IGRhdGEucHJvY2Vzc0lkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkIikpIHJlc3VsdC5zY3JpcHRQcm9jZXNzZWQgPSBkYXRhLnNjcmlwdFByb2Nlc3NlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInNjcmlwdFByb2Nlc3NlZEF0IikpIHJlc3VsdC5zY3JpcHRQcm9jZXNzZWRBdCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkQXQ7CgogICAgICAgIC8vIHRpbWVyU3RhcnRlZEF0OiB0aW1lci13YWl0LXN0YXRlJ2UgZ2lyaWxkaWdpIGFuLiBTY2hlZHVsZWQgdHJhbnNpdGlvbiAoVGltZXIgVGFzayB0aXAgOSkKICAgICAgICAvLyAzIHNuIHNvbnJhIHN0YXJ0LWZsb3ctc3RhdGUnZSBnZWNpcmlyOyB0ZXN0bGVyIGh1bWFuLXRhc2stc3RhdGUndGUgYXR0cmlidXRlcyBva3V5dXAKICAgICAgICAvLyAoRGF0ZVRpbWUuVXRjTm93IC0gdGltZXJTdGFydGVkQXQpID49IH4zIHNuIG9sbWFzaW5pIGRvZ3J1bGFyIC0+IFRpbWVyIFRhc2sgZ2VyY2VrdGVuIGJla2xlbWlzIGRlbWVrdGlyLgogICAgICAgIHJlc3VsdC50aW1lclN0YXJ0ZWRBdCA9IG5vdy5Ub1N0cmluZygibyIpOwogICAgICAgIHJlc3VsdC50aW1lckV4cGVjdGVkU2Vjb25kcyA9IDM7CiAgICAgICAgcmVzdWx0LnRpbWVyQ29tcGxldGVkID0gZmFsc2U7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCQiVGltZXJTdGFydE1hcHBpbmc6IHRpbWVyU3RhcnRlZEF0PXtyZXN1bHQudGltZXJTdGFydGVkQXR9LCB0aW1lckV4cGVjdGVkU2Vjb25kcz0zIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } ], "onExits": [], "transitions": [ { - "key": "auto-to-start-flow", + "key": "scheduled-to-start-flow", "target": "start-flow-state", - "triggerType": 1, + "triggerType": 2, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Auto to Start Flow" + "label": "Scheduled to Start Flow" }, { "language": "tr-TR", - "label": "Flow Baslatma Otomatik Gecis" + "label": "Zamanlanmis Flow Baslatma Gecisi" } ], - "rule": { - "location": "./src/shared/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" + "timer": { + "location": "./src/task-execution-test-workflow/TimerScheduleMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFRpbWVyU2NoZWR1bGVNYXBwaW5nIDogU2NyaXB0QmFzZSwgSVRpbWVyTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxUaW1lclNjaGVkdWxlPiBIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICAvLyBTY2hlZHVsZWQgdHJhbnNpdGlvbiAodHJpZ2dlclR5cGU6IDIpIGljaW4gVGltZXIgVGFzayAodGlwIDkpIGJ1cmFkYSB1cmV0aWxpci4KICAgICAgICAvLyAzIHNuIHNvbnJhc2luaSBkb25kdXJlcmVrIHJ1bnRpbWUnaW4gYmVrbGVtZXNpbmkgc2FnbGl5b3J1ei4KICAgICAgICB2YXIgZmlyZUF0ID0gRGF0ZVRpbWUuVXRjTm93LkFkZFNlY29uZHMoMyk7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oJCJUaW1lclNjaGVkdWxlTWFwcGluZzogc2NoZWR1bGluZyBhdCB7ZmlyZUF0Om99ICgzIHNlY29uZHMgZnJvbSBub3cpIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChUaW1lclNjaGVkdWxlLkZyb21EYXRlVGltZShmaXJlQXQpKTsKICAgIH0KfQo=" }, "onExecutionTasks": [] } @@ -267,7 +267,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/StartFlowMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBTdGFydEZsb3dNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIC8vIFN0YXJ0VGFzayAodGlwIDExKSB5ZW5pIGJpciBoZWRlZiB3b3JrZmxvdyBpbnN0YW5jZSdpIGFjYXIuCiAgICAgICAgLy8gQm9keSB1emVyaW5kZW4gaGVkZWYgaW5zdGFuY2UgZGF0YSdzaW5hIHBhcmVudCByZWZlcmFuc2kgdmUga2F5bmFrIG5vdHUgeWF6aXlvcnV6IGtpCiAgICAgICAgLy8gdGVzdGxlciBoZWRlZiBHZXRJbnN0YW5jZSBhdHRyaWJ1dGVzJ3VuZGEgcGFyZW50IHppbmNpcmluaSBkb2dydWxheWFiaWxzaW4uCiAgICAgICAgdmFyIHN0YXJ0VGFzayA9ICh0YXNrIGFzIFN0YXJ0VGFzaykhOwoKICAgICAgICB2YXIgYm9keSA9IG5ldwogICAgICAgIHsKICAgICAgICAgICAgc291cmNlID0gInRhc2stZXhlY3V0aW9uLXRlc3QiLAogICAgICAgICAgICBwYXJlbnRJbnN0YW5jZUlkID0gY29udGV4dC5JbnN0YW5jZS5JZCwKICAgICAgICAgICAgbm90ZSA9ICJ0aGlzIGlzIGEgc3RhcnRmbG93IGZyb20gdGFzay1leGVjdXRpb24tdGVzdC13b3JrZmxvdyIKICAgICAgICB9OwogICAgICAgIHN0YXJ0VGFzay5TZXRCb2R5KGJvZHkpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU3RhcnRGbG93TWFwcGluZyBJbnB1dEhhbmRsZXI6IGJvZHkgc2V0IHdpdGggcGFyZW50SW5zdGFuY2VJZC9zb3VyY2Uvbm90ZSIpOwogICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UoKTsKICAgIH0KCiAgICBwdWJsaWMgYXN5bmMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgdmFyIHRhc2tSZXNwb25zZSA9IGNvbnRleHQuQm9keTsKICAgICAgICB2YXIgcmVzcG9uc2VCb2R5ID0gdGFza1Jlc3BvbnNlLmRhdGE7CgogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwVGFza0NvbXBsZXRlZCIpKSByZXN1bHQuaHR0cFRhc2tDb21wbGV0ZWQgPSBkYXRhLmh0dHBUYXNrQ29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicHJvY2Vzc0lkIikpIHJlc3VsdC5wcm9jZXNzSWQgPSBkYXRhLnByb2Nlc3NJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInNjcmlwdFByb2Nlc3NlZCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkID0gZGF0YS5zY3JpcHRQcm9jZXNzZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcm9zc1dvcmtmbG93Q29tcGxldGVkIikpIHJlc3VsdC5jcm9zc1dvcmtmbG93Q29tcGxldGVkID0gZGF0YS5jcm9zc1dvcmtmbG93Q29tcGxldGVkOwoKICAgICAgICByZXN1bHQuc3RhcnRGbG93Q29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gdGFza1Jlc3BvbnNlLmlzU3VjY2VzczsKCiAgICAgICAgaWYgKHJlc3BvbnNlQm9keSAhPSBudWxsKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgImlkIikpCiAgICAgICAgICAgICAgICByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSByZXNwb25zZUJvZHkuaWQ7CiAgICAgICAgICAgIGVsc2UgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgImluc3RhbmNlSWQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5zdGFydGVkSW5zdGFuY2VJZCA9IHJlc3BvbnNlQm9keS5pbnN0YW5jZUlkOwogICAgICAgIH0KCiAgICAgICAgTG9nSW5mb3JtYXRpb24oJCJTdGFydEZsb3dNYXBwaW5nIE91dHB1dEhhbmRsZXI6IGlzU3VjY2Vzcz17dGFza1Jlc3BvbnNlLmlzU3VjY2Vzc30iKTsKICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9OwogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBTdGFydEZsb3dNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIC8vIFN0YXJ0VGFzayAodGlwIDExKSB5ZW5pIGJpciBoZWRlZiB3b3JrZmxvdyBpbnN0YW5jZSdpIGFjYXIuCiAgICAgICAgLy8gQm9keSB1emVyaW5kZW4gaGVkZWYgaW5zdGFuY2UgZGF0YSdzaW5hIHBhcmVudCByZWZlcmFuc2kgdmUga2F5bmFrIG5vdHUgeWF6aXlvcnV6IGtpCiAgICAgICAgLy8gdGVzdGxlciBoZWRlZiBHZXRJbnN0YW5jZSBhdHRyaWJ1dGVzJ3VuZGEgcGFyZW50IHppbmNpcmluaSBkb2dydWxheWFiaWxzaW4uCiAgICAgICAgdmFyIHN0YXJ0VGFzayA9ICh0YXNrIGFzIFN0YXJ0VGFzaykhOwoKICAgICAgICB2YXIgYm9keSA9IG5ldwogICAgICAgIHsKICAgICAgICAgICAgc291cmNlID0gInRhc2stZXhlY3V0aW9uLXRlc3QiLAogICAgICAgICAgICBwYXJlbnRJbnN0YW5jZUlkID0gY29udGV4dC5JbnN0YW5jZS5JZCwKICAgICAgICAgICAgbm90ZSA9ICJ0aGlzIGlzIGEgc3RhcnRmbG93IGZyb20gdGFzay1leGVjdXRpb24tdGVzdC13b3JrZmxvdyIKICAgICAgICB9OwogICAgICAgIHN0YXJ0VGFzay5TZXRCb2R5KGJvZHkpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU3RhcnRGbG93TWFwcGluZyBJbnB1dEhhbmRsZXI6IGJvZHkgc2V0IHdpdGggcGFyZW50SW5zdGFuY2VJZC9zb3VyY2Uvbm90ZSIpOwogICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UoKTsKICAgIH0KCiAgICBwdWJsaWMgYXN5bmMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgdmFyIHRhc2tSZXNwb25zZSA9IGNvbnRleHQuQm9keTsKICAgICAgICB2YXIgcmVzcG9uc2VCb2R5ID0gdGFza1Jlc3BvbnNlLmRhdGE7CgogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwVGFza0NvbXBsZXRlZCIpKSByZXN1bHQuaHR0cFRhc2tDb21wbGV0ZWQgPSBkYXRhLmh0dHBUYXNrQ29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicHJvY2Vzc0lkIikpIHJlc3VsdC5wcm9jZXNzSWQgPSBkYXRhLnByb2Nlc3NJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInNjcmlwdFByb2Nlc3NlZCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkID0gZGF0YS5zY3JpcHRQcm9jZXNzZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0aW1lclN0YXJ0ZWRBdCIpKSByZXN1bHQudGltZXJTdGFydGVkQXQgPSBkYXRhLnRpbWVyU3RhcnRlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGltZXJFeHBlY3RlZFNlY29uZHMiKSkgcmVzdWx0LnRpbWVyRXhwZWN0ZWRTZWNvbmRzID0gZGF0YS50aW1lckV4cGVjdGVkU2Vjb25kczsKCiAgICAgICAgcmVzdWx0LnN0YXJ0Rmxvd0NvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnN0YXJ0Rmxvd0lzU3VjY2VzcyA9IHRhc2tSZXNwb25zZS5pc1N1Y2Nlc3M7CgogICAgICAgIGlmIChyZXNwb25zZUJvZHkgIT0gbnVsbCkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShyZXNwb25zZUJvZHksICJpZCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnN0YXJ0ZWRJbnN0YW5jZUlkID0gcmVzcG9uc2VCb2R5LmlkOwogICAgICAgICAgICBlbHNlIGlmIChIYXNQcm9wZXJ0eShyZXNwb25zZUJvZHksICJpbnN0YW5jZUlkIikpCiAgICAgICAgICAgICAgICByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSByZXNwb25zZUJvZHkuaW5zdGFuY2VJZDsKICAgICAgICB9CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCQiU3RhcnRGbG93TWFwcGluZyBPdXRwdXRIYW5kbGVyOiBpc1N1Y2Nlc3M9e3Rhc2tSZXNwb25zZS5pc1N1Y2Nlc3N9Iik7CiAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfTsKICAgIH0KfQo=" } } ], @@ -324,7 +324,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/GetInstanceDataMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBHZXRJbnN0YW5jZURhdGFNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBnZXREYXRhVGFzayA9ICh0YXNrIGFzIEdldEluc3RhbmNlRGF0YVRhc2spITsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKQogICAgICAgIHsKICAgICAgICAgICAgdmFyIHRhcmdldElkID0gZGF0YS5zdGFydGVkSW5zdGFuY2VJZD8uVG9TdHJpbmcoKTsKICAgICAgICAgICAgZ2V0RGF0YVRhc2suU2V0SW5zdGFuY2UodGFyZ2V0SWQpOwogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkdldEluc3RhbmNlRGF0YU1hcHBpbmcgSW5wdXRIYW5kbGVyOiB0YXJnZXQgaWQ9e3RhcmdldElkfSIpOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZSgpOwogICAgfQoKICAgIHB1YmxpYyBhc3luYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICB2YXIgdGFza1Jlc3BvbnNlID0gY29udGV4dC5Cb2R5OwogICAgICAgIHZhciByZXNwb25zZUJvZHkgPSB0YXNrUmVzcG9uc2UuZGF0YTsKCiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJwcm9jZXNzSWQiKSkgcmVzdWx0LnByb2Nlc3NJZCA9IGRhdGEucHJvY2Vzc0lkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkIikpIHJlc3VsdC5zY3JpcHRQcm9jZXNzZWQgPSBkYXRhLnNjcmlwdFByb2Nlc3NlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyb3NzV29ya2Zsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQgPSBkYXRhLmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydEZsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LnN0YXJ0Rmxvd0NvbXBsZXRlZCA9IGRhdGEuc3RhcnRGbG93Q29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEluc3RhbmNlSWQiKSkgcmVzdWx0LnN0YXJ0ZWRJbnN0YW5jZUlkID0gZGF0YS5zdGFydGVkSW5zdGFuY2VJZDsKCiAgICAgICAgcmVzdWx0LmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LmdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyA9IHRhc2tSZXNwb25zZS5pc1N1Y2Nlc3M7CgogICAgICAgIGlmIChyZXNwb25zZUJvZHkgIT0gbnVsbCkKICAgICAgICAgICAgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IHJlc3BvbnNlQm9keTsKCiAgICAgICAgTG9nSW5mb3JtYXRpb24oJCJHZXRJbnN0YW5jZURhdGFNYXBwaW5nIE91dHB1dEhhbmRsZXI6IGlzU3VjY2Vzcz17dGFza1Jlc3BvbnNlLmlzU3VjY2Vzc30iKTsKICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9OwogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBHZXRJbnN0YW5jZURhdGFNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBnZXREYXRhVGFzayA9ICh0YXNrIGFzIEdldEluc3RhbmNlRGF0YVRhc2spITsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKQogICAgICAgIHsKICAgICAgICAgICAgdmFyIHRhcmdldElkID0gZGF0YS5zdGFydGVkSW5zdGFuY2VJZD8uVG9TdHJpbmcoKTsKICAgICAgICAgICAgZ2V0RGF0YVRhc2suU2V0SW5zdGFuY2UodGFyZ2V0SWQpOwogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkdldEluc3RhbmNlRGF0YU1hcHBpbmcgSW5wdXRIYW5kbGVyOiB0YXJnZXQgaWQ9e3RhcmdldElkfSIpOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZSgpOwogICAgfQoKICAgIHB1YmxpYyBhc3luYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICB2YXIgdGFza1Jlc3BvbnNlID0gY29udGV4dC5Cb2R5OwogICAgICAgIHZhciByZXNwb25zZUJvZHkgPSB0YXNrUmVzcG9uc2UuZGF0YTsKCiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJwcm9jZXNzSWQiKSkgcmVzdWx0LnByb2Nlc3NJZCA9IGRhdGEucHJvY2Vzc0lkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkIikpIHJlc3VsdC5zY3JpcHRQcm9jZXNzZWQgPSBkYXRhLnNjcmlwdFByb2Nlc3NlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRpbWVyU3RhcnRlZEF0IikpIHJlc3VsdC50aW1lclN0YXJ0ZWRBdCA9IGRhdGEudGltZXJTdGFydGVkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0aW1lckV4cGVjdGVkU2Vjb25kcyIpKSByZXN1bHQudGltZXJFeHBlY3RlZFNlY29uZHMgPSBkYXRhLnRpbWVyRXhwZWN0ZWRTZWNvbmRzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRJbnN0YW5jZUlkIikpIHJlc3VsdC5zdGFydGVkSW5zdGFuY2VJZCA9IGRhdGEuc3RhcnRlZEluc3RhbmNlSWQ7CgogICAgICAgIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3MgPSB0YXNrUmVzcG9uc2UuaXNTdWNjZXNzOwoKICAgICAgICBpZiAocmVzcG9uc2VCb2R5ICE9IG51bGwpCiAgICAgICAgICAgIHJlc3VsdC5yZW1vdGVJbnN0YW5jZURhdGEgPSByZXNwb25zZUJvZHk7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCQiR2V0SW5zdGFuY2VEYXRhTWFwcGluZyBPdXRwdXRIYW5kbGVyOiBpc1N1Y2Nlc3M9e3Rhc2tSZXNwb25zZS5pc1N1Y2Nlc3N9Iik7CiAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfTsKICAgIH0KfQo=" } } ], @@ -437,7 +437,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/TriggerTransitionMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBUcmlnZ2VyVHJhbnNpdGlvbk1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRpcmVjdFRyaWdnZXIgPSAodGFzayBhcyBEaXJlY3RUcmlnZ2VyVGFzaykhOwogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRJbnN0YW5jZUlkIikpCiAgICAgICAgewogICAgICAgICAgICB2YXIgdGFyZ2V0SWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkPy5Ub1N0cmluZygpOwogICAgICAgICAgICBkaXJlY3RUcmlnZ2VyLlNldEluc3RhbmNlKHRhcmdldElkISk7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCQiVHJpZ2dlclRyYW5zaXRpb25NYXBwaW5nIElucHV0SGFuZGxlcjogU2V0SW5zdGFuY2Uge3RhcmdldElkfSIpOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyb3NzV29ya2Zsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQgPSBkYXRhLmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcm9zc1dvcmtmbG93QXQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dBdCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0F0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJodW1hblRhc2siKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IGRhdGEudGFza1Jlc3VsdHMuaHVtYW5UYXNrOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24uY29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24uZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiVHJpZ2dlclRyYW5zaXRpb25NYXBwaW5nIGNvbXBsZXRlZCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBUcmlnZ2VyVHJhbnNpdGlvbk1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRpcmVjdFRyaWdnZXIgPSAodGFzayBhcyBEaXJlY3RUcmlnZ2VyVGFzaykhOwogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRJbnN0YW5jZUlkIikpCiAgICAgICAgewogICAgICAgICAgICB2YXIgdGFyZ2V0SWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkPy5Ub1N0cmluZygpOwogICAgICAgICAgICBkaXJlY3RUcmlnZ2VyLlNldEluc3RhbmNlKHRhcmdldElkISk7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCQiVHJpZ2dlclRyYW5zaXRpb25NYXBwaW5nIElucHV0SGFuZGxlcjogU2V0SW5zdGFuY2Uge3RhcmdldElkfSIpOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRpbWVyU3RhcnRlZEF0IikpIHJlc3VsdC50aW1lclN0YXJ0ZWRBdCA9IGRhdGEudGltZXJTdGFydGVkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0aW1lckV4cGVjdGVkU2Vjb25kcyIpKSByZXN1bHQudGltZXJFeHBlY3RlZFNlY29uZHMgPSBkYXRhLnRpbWVyRXhwZWN0ZWRTZWNvbmRzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJodW1hblRhc2siKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IGRhdGEudGFza1Jlc3VsdHMuaHVtYW5UYXNrOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24uY29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24uZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiVHJpZ2dlclRyYW5zaXRpb25NYXBwaW5nIGNvbXBsZXRlZCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" } } ], @@ -494,7 +494,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/SubProcessMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBTdWJQcm9jZXNzTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICAvLyBTdWJQcm9jZXNzVGFzayAodGlwIDE0KSBmaXJlLWFuZC1mb3JnZXQgYmlyIGFsdCBpbnN0YW5jZSBhY2FyLgogICAgICAgIC8vIEJvZHkgdXplcmluZGVuIHN1YnByb2Nlc3MnaW4gaW5zdGFuY2UgZGF0YSdzaW5hIHBhcmVudCByZWZlcmFuc2kgdmUga2F5bmFrIG5vdHUgeWF6aXlvcnV6OwogICAgICAgIC8vIHRlc3RsZXIgYnUgYWxhbmxhcmkgc3VicHJvY2VzcyBHZXRJbnN0YW5jZSBhdHRyaWJ1dGVzJ3VuZGEgZG9ncnVsYXlhYmlsc2luLgogICAgICAgIHZhciBzdWJQcm9jZXNzVGFzayA9ICh0YXNrIGFzIFN1YlByb2Nlc3NUYXNrKSE7CgogICAgICAgIHZhciBib2R5ID0gbmV3CiAgICAgICAgewogICAgICAgICAgICBzb3VyY2UgPSAidGFzay1leGVjdXRpb24tdGVzdCIsCiAgICAgICAgICAgIHBhcmVudEluc3RhbmNlSWQgPSBjb250ZXh0Lkluc3RhbmNlLklkLAogICAgICAgICAgICBub3RlID0gInRoaXMgaXMgYSBzdWJwcm9jZXNzIHN0YXJ0ZWQgZnJvbSB0YXNrLWV4ZWN1dGlvbi10ZXN0LXdvcmtmbG93IgogICAgICAgIH07CiAgICAgICAgc3ViUHJvY2Vzc1Rhc2suU2V0Qm9keShib2R5KTsKCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlN1YlByb2Nlc3NNYXBwaW5nIElucHV0SGFuZGxlcjogYm9keSBzZXQgd2l0aCBwYXJlbnRJbnN0YW5jZUlkL3NvdXJjZS9ub3RlIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIHZhciB0YXNrUmVzcG9uc2UgPSBjb250ZXh0LkJvZHk7CiAgICAgICAgdmFyIHJlc3BvbnNlQm9keSA9IHRhc2tSZXNwb25zZT8uZGF0YTsKCiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkQXQiKSkgcmVzdWx0LnN0YXJ0ZWRBdCA9IGRhdGEuc3RhcnRlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBTdGF0dXNDb2RlIikpIHJlc3VsdC5odHRwU3RhdHVzQ29kZSA9IGRhdGEuaHR0cFN0YXR1c0NvZGU7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwSXNTdWNjZXNzIikpIHJlc3VsdC5odHRwSXNTdWNjZXNzID0gZGF0YS5odHRwSXNTdWNjZXNzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicHJvY2Vzc0lkIikpIHJlc3VsdC5wcm9jZXNzSWQgPSBkYXRhLnByb2Nlc3NJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInNjcmlwdFByb2Nlc3NlZCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkID0gZGF0YS5zY3JpcHRQcm9jZXNzZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWRBdCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkQXQgPSBkYXRhLnNjcmlwdFByb2Nlc3NlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCIpKSByZXN1bHQuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyb3NzV29ya2Zsb3dBdCIpKSByZXN1bHQuY3Jvc3NXb3JrZmxvd0F0ID0gZGF0YS5jcm9zc1dvcmtmbG93QXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydEZsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LnN0YXJ0Rmxvd0NvbXBsZXRlZCA9IGRhdGEuc3RhcnRGbG93Q29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93SXNTdWNjZXNzIikpIHJlc3VsdC5zdGFydEZsb3dJc1N1Y2Nlc3MgPSBkYXRhLnN0YXJ0Rmxvd0lzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRJbnN0YW5jZUlkIikpIHJlc3VsdC5zdGFydGVkSW5zdGFuY2VJZCA9IGRhdGEuc3RhcnRlZEluc3RhbmNlSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJnZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQiKSkgcmVzdWx0LmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZCA9IGRhdGEuZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3MgPSBkYXRhLmdldEluc3RhbmNlRGF0YUlzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInJlbW90ZUluc3RhbmNlRGF0YSIpKSByZXN1bHQucmVtb3RlSW5zdGFuY2VEYXRhID0gZGF0YS5yZW1vdGVJbnN0YW5jZURhdGE7CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0YXNrUmVzdWx0cyIpKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInRyaWdnZXJUcmFuc2l0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAic3VicHJvY2VzcyIpKSByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2VzcyA9IGRhdGEudGFza1Jlc3VsdHMuc3VicHJvY2VzczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJnZXRJbnN0YW5jZXMiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IGRhdGEudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImh1bWFuVGFzayIpKSByZXN1bHQudGFza1Jlc3VsdHMuaHVtYW5UYXNrID0gZGF0YS50YXNrUmVzdWx0cy5odW1hblRhc2s7CiAgICAgICAgfQoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2VzcyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MuY29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2Vzcy5leGVjdXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIC8vIFN1YlByb2Nlc3MgeWFuaXRpIHJ1bnRpbWUgZG9rdW1hbmluYSBnb3JlIGRhdGEuaWQgKHllbmkgaW5zdGFuY2UgaWQpIGRvbmVyLgogICAgICAgIC8vIEVza2kgeWFuaXRsYXJkYSBkYXRhLmluc3RhbmNlSWQgZ29ydWxlYmlsaXI7IGlraXNpbmkgZGUgZ3V2ZW5saSBzZWtpbGRlIGRlbmUuCiAgICAgICAgaWYgKHJlc3BvbnNlQm9keSAhPSBudWxsKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgImlkIikpCiAgICAgICAgICAgICAgICByZXN1bHQuc3VicHJvY2Vzc0luc3RhbmNlSWQgPSByZXNwb25zZUJvZHkuaWQ7CiAgICAgICAgICAgIGVsc2UgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgImluc3RhbmNlSWQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5zdWJwcm9jZXNzSW5zdGFuY2VJZCA9IHJlc3BvbnNlQm9keS5pbnN0YW5jZUlkOwogICAgICAgIH0KCiAgICAgICAgaWYgKHRhc2tSZXNwb25zZSAhPSBudWxsICYmIEhhc1Byb3BlcnR5KHRhc2tSZXNwb25zZSwgImlzU3VjY2VzcyIpKQogICAgICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2Vzcy5pc1N1Y2Nlc3MgPSB0YXNrUmVzcG9uc2UuaXNTdWNjZXNzOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU3ViUHJvY2Vzc01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBTdWJQcm9jZXNzTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICAvLyBTdWJQcm9jZXNzVGFzayAodGlwIDE0KSBmaXJlLWFuZC1mb3JnZXQgYmlyIGFsdCBpbnN0YW5jZSBhY2FyLgogICAgICAgIC8vIEJvZHkgdXplcmluZGVuIHN1YnByb2Nlc3MnaW4gaW5zdGFuY2UgZGF0YSdzaW5hIHBhcmVudCByZWZlcmFuc2kgdmUga2F5bmFrIG5vdHUgeWF6aXlvcnV6OwogICAgICAgIC8vIHRlc3RsZXIgYnUgYWxhbmxhcmkgc3VicHJvY2VzcyBHZXRJbnN0YW5jZSBhdHRyaWJ1dGVzJ3VuZGEgZG9ncnVsYXlhYmlsc2luLgogICAgICAgIHZhciBzdWJQcm9jZXNzVGFzayA9ICh0YXNrIGFzIFN1YlByb2Nlc3NUYXNrKSE7CgogICAgICAgIHZhciBib2R5ID0gbmV3CiAgICAgICAgewogICAgICAgICAgICBzb3VyY2UgPSAidGFzay1leGVjdXRpb24tdGVzdCIsCiAgICAgICAgICAgIHBhcmVudEluc3RhbmNlSWQgPSBjb250ZXh0Lkluc3RhbmNlLklkLAogICAgICAgICAgICBub3RlID0gInRoaXMgaXMgYSBzdWJwcm9jZXNzIHN0YXJ0ZWQgZnJvbSB0YXNrLWV4ZWN1dGlvbi10ZXN0LXdvcmtmbG93IgogICAgICAgIH07CiAgICAgICAgc3ViUHJvY2Vzc1Rhc2suU2V0Qm9keShib2R5KTsKCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlN1YlByb2Nlc3NNYXBwaW5nIElucHV0SGFuZGxlcjogYm9keSBzZXQgd2l0aCBwYXJlbnRJbnN0YW5jZUlkL3NvdXJjZS9ub3RlIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIHZhciB0YXNrUmVzcG9uc2UgPSBjb250ZXh0LkJvZHk7CiAgICAgICAgdmFyIHJlc3BvbnNlQm9keSA9IHRhc2tSZXNwb25zZT8uZGF0YTsKCiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkQXQiKSkgcmVzdWx0LnN0YXJ0ZWRBdCA9IGRhdGEuc3RhcnRlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBTdGF0dXNDb2RlIikpIHJlc3VsdC5odHRwU3RhdHVzQ29kZSA9IGRhdGEuaHR0cFN0YXR1c0NvZGU7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwSXNTdWNjZXNzIikpIHJlc3VsdC5odHRwSXNTdWNjZXNzID0gZGF0YS5odHRwSXNTdWNjZXNzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicHJvY2Vzc0lkIikpIHJlc3VsdC5wcm9jZXNzSWQgPSBkYXRhLnByb2Nlc3NJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInNjcmlwdFByb2Nlc3NlZCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkID0gZGF0YS5zY3JpcHRQcm9jZXNzZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWRBdCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkQXQgPSBkYXRhLnNjcmlwdFByb2Nlc3NlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGltZXJTdGFydGVkQXQiKSkgcmVzdWx0LnRpbWVyU3RhcnRlZEF0ID0gZGF0YS50aW1lclN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRpbWVyRXhwZWN0ZWRTZWNvbmRzIikpIHJlc3VsdC50aW1lckV4cGVjdGVkU2Vjb25kcyA9IGRhdGEudGltZXJFeHBlY3RlZFNlY29uZHM7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydEZsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LnN0YXJ0Rmxvd0NvbXBsZXRlZCA9IGRhdGEuc3RhcnRGbG93Q29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93SXNTdWNjZXNzIikpIHJlc3VsdC5zdGFydEZsb3dJc1N1Y2Nlc3MgPSBkYXRhLnN0YXJ0Rmxvd0lzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRJbnN0YW5jZUlkIikpIHJlc3VsdC5zdGFydGVkSW5zdGFuY2VJZCA9IGRhdGEuc3RhcnRlZEluc3RhbmNlSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJnZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQiKSkgcmVzdWx0LmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZCA9IGRhdGEuZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3MgPSBkYXRhLmdldEluc3RhbmNlRGF0YUlzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInJlbW90ZUluc3RhbmNlRGF0YSIpKSByZXN1bHQucmVtb3RlSW5zdGFuY2VEYXRhID0gZGF0YS5yZW1vdGVJbnN0YW5jZURhdGE7CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0YXNrUmVzdWx0cyIpKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInRyaWdnZXJUcmFuc2l0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAic3VicHJvY2VzcyIpKSByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2VzcyA9IGRhdGEudGFza1Jlc3VsdHMuc3VicHJvY2VzczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJnZXRJbnN0YW5jZXMiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IGRhdGEudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImh1bWFuVGFzayIpKSByZXN1bHQudGFza1Jlc3VsdHMuaHVtYW5UYXNrID0gZGF0YS50YXNrUmVzdWx0cy5odW1hblRhc2s7CiAgICAgICAgfQoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2VzcyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MuY29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2Vzcy5leGVjdXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIC8vIFN1YlByb2Nlc3NUYXNrIHlhbml0aW5pbiBnZXJjZWsgc2VydmlzICJkYXRhIiBnb3ZkZXNpIHBhcmVudCBhdHRyaWJ1dGVzLnN1YnByb2Nlc3NEYXRhIGFsdGluZGEgc2FrbGFuaXIKICAgICAgICAvLyAoaW50ZWdyYXRpb24gdGVzdCB2ZSBvcGVyYXN5b25lbCBpbmNlbGVtZSBpY2luKS4gQXlyaWNhIHN1YnByb2Nlc3NJbnN0YW5jZUlkIGtvbGF5IGVyaXNpbSBpY2luIGF5cmkgeWF6aWxpci4KICAgICAgICBpZiAocmVzcG9uc2VCb2R5ICE9IG51bGwpCiAgICAgICAgewogICAgICAgICAgICBkeW5hbWljIHNuYXBzaG90ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgImlkIikpIHNuYXBzaG90LmlkID0gcmVzcG9uc2VCb2R5LmlkOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkocmVzcG9uc2VCb2R5LCAiaW5zdGFuY2VJZCIpKSBzbmFwc2hvdC5pbnN0YW5jZUlkID0gcmVzcG9uc2VCb2R5Lmluc3RhbmNlSWQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShyZXNwb25zZUJvZHksICJzdGF0ZSIpKSBzbmFwc2hvdC5zdGF0ZSA9IHJlc3BvbnNlQm9keS5zdGF0ZTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgImxhdW5jaGVkIikpIHNuYXBzaG90LmxhdW5jaGVkID0gcmVzcG9uc2VCb2R5LmxhdW5jaGVkOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkocmVzcG9uc2VCb2R5LCAic3RhdHVzIikpIHNuYXBzaG90LnN0YXR1cyA9IHJlc3BvbnNlQm9keS5zdGF0dXM7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShyZXNwb25zZUJvZHksICJzdGF0dXNDb2RlIikpIHNuYXBzaG90LnN0YXR1c0NvZGUgPSByZXNwb25zZUJvZHkuc3RhdHVzQ29kZTsKICAgICAgICAgICAgcmVzdWx0LnN1YnByb2Nlc3NEYXRhID0gc25hcHNob3Q7CgogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkocmVzcG9uc2VCb2R5LCAiaWQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5zdWJwcm9jZXNzSW5zdGFuY2VJZCA9IHJlc3BvbnNlQm9keS5pZDsKICAgICAgICAgICAgZWxzZSBpZiAoSGFzUHJvcGVydHkocmVzcG9uc2VCb2R5LCAiaW5zdGFuY2VJZCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnN1YnByb2Nlc3NJbnN0YW5jZUlkID0gcmVzcG9uc2VCb2R5Lmluc3RhbmNlSWQ7CiAgICAgICAgfQoKICAgICAgICBpZiAodGFza1Jlc3BvbnNlICE9IG51bGwgJiYgSGFzUHJvcGVydHkodGFza1Jlc3BvbnNlLCAiaXNTdWNjZXNzIikpCiAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzLmlzU3VjY2VzcyA9IHRhc2tSZXNwb25zZS5pc1N1Y2Nlc3M7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCJTdWJQcm9jZXNzTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } ], @@ -551,7 +551,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/GetInstancesMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEdldEluc3RhbmNlc01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyb3NzV29ya2Zsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQgPSBkYXRhLmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcm9zc1dvcmtmbG93QXQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dBdCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0F0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJodW1hblRhc2siKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IGRhdGEudGFza1Jlc3VsdHMuaHVtYW5UYXNrOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcy5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMuZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICB2YXIgdGFza1Jlc3BvbnNlID0gY29udGV4dC5Cb2R5OwogICAgICAgIGlmICh0YXNrUmVzcG9uc2UgIT0gbnVsbCAmJiBIYXNQcm9wZXJ0eSh0YXNrUmVzcG9uc2UsICJkYXRhIikpCiAgICAgICAgewogICAgICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzLnJlc3BvbnNlUmVjZWl2ZWQgPSB0cnVlOwogICAgICAgIH0KCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkdldEluc3RhbmNlc01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEdldEluc3RhbmNlc01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRpbWVyU3RhcnRlZEF0IikpIHJlc3VsdC50aW1lclN0YXJ0ZWRBdCA9IGRhdGEudGltZXJTdGFydGVkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0aW1lckV4cGVjdGVkU2Vjb25kcyIpKSByZXN1bHQudGltZXJFeHBlY3RlZFNlY29uZHMgPSBkYXRhLnRpbWVyRXhwZWN0ZWRTZWNvbmRzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJodW1hblRhc2siKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IGRhdGEudGFza1Jlc3VsdHMuaHVtYW5UYXNrOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcy5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMuZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICB2YXIgdGFza1Jlc3BvbnNlID0gY29udGV4dC5Cb2R5OwogICAgICAgIGlmICh0YXNrUmVzcG9uc2UgIT0gbnVsbCAmJiBIYXNQcm9wZXJ0eSh0YXNrUmVzcG9uc2UsICJkYXRhIikpCiAgICAgICAgewogICAgICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzLnJlc3BvbnNlUmVjZWl2ZWQgPSB0cnVlOwogICAgICAgIH0KCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkdldEluc3RhbmNlc01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } ], @@ -608,7 +608,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/HumanTaskMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEh1bWFuVGFza01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyb3NzV29ya2Zsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQgPSBkYXRhLmNyb3NzV29ya2Zsb3dDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcm9zc1dvcmtmbG93QXQiKSkgcmVzdWx0LmNyb3NzV29ya2Zsb3dBdCA9IGRhdGEuY3Jvc3NXb3JrZmxvd0F0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJodW1hblRhc2siKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IGRhdGEudGFza1Jlc3VsdHMuaHVtYW5UYXNrOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzay5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5odW1hblRhc2suYXBwcm92ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiSHVtYW5UYXNrTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEh1bWFuVGFza01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRpbWVyU3RhcnRlZEF0IikpIHJlc3VsdC50aW1lclN0YXJ0ZWRBdCA9IGRhdGEudGltZXJTdGFydGVkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0aW1lckV4cGVjdGVkU2Vjb25kcyIpKSByZXN1bHQudGltZXJFeHBlY3RlZFNlY29uZHMgPSBkYXRhLnRpbWVyRXhwZWN0ZWRTZWNvbmRzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJodW1hblRhc2siKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IGRhdGEudGFza1Jlc3VsdHMuaHVtYW5UYXNrOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzay5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5odW1hblRhc2suYXBwcm92ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiSHVtYW5UYXNrTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } ], diff --git a/doc/integration-test-documentation.md b/doc/integration-test-documentation.md index 6d9c9d7..1390dae 100644 --- a/doc/integration-test-documentation.md +++ b/doc/integration-test-documentation.md @@ -91,7 +91,7 @@ lifecycle-transitions-test-workflow (F) ├── auto-passed-state (Intermediate, stateType:2) │ └── Transitions: │ ├── scheduled-timer-transition (Scheduled, triggerType:2) → timer-triggered-state -│ │ └── timer: ShortTimerMapping.csx (ITimerMapping, 10 saniye) +│ │ └── timer: ShortTimerMapping.csx (ITimerMapping, 6 saniye) │ ├── cancel-schedule-manually (Manual, triggerType:0) → pre-complete-state │ │ └── (zamanlayici schedule'ini manuel transition ile iptal senaryosu) │ └── reschedule-timer (Manual, triggerType:0, target: auto-passed-state) @@ -140,7 +140,7 @@ lifecycle-transitions-test-workflow (F) | Cancel transition | Workflow iptal mekanizmasi | cancel-workflow → terminated-state | | **Exit transition (`attributes.exit`)** | Belirli state'lerde cikis; hedef terminated; IMapping ile exitExecuted | exit-workflow, ExitMapping.csx | | **Schedule iptal (manuel)** | auto-passed-state'ten manuel gecisle timer beklemeden pre-complete | cancel-schedule-manually | -| **Timer reschedule (self-loop)** | Ayni state'e manuel gecis ile zamanlayici reschedule | reschedule-timer → **auto-passed-state**; sonra yeniden kurulan `scheduled-timer-transition` (~10s) → timer-triggered → auto → pre-complete | +| **Timer reschedule (self-loop)** | Ayni state'e manuel gecis ile zamanlayici reschedule | reschedule-timer → **auto-passed-state**; sonra yeniden kurulan `scheduled-timer-transition` (~6s) → timer-triggered → auto → pre-complete | | **queryRoles (workflow)** | authorize endpoint ile workflow datasi sorgu yetkisi | attributes.queryRoles, test-viewer | | **queryRoles (state)** | State bazli allow/deny override | **pre-complete-state** queryRoles (`processing-state` auto gecisli; testte cancel-schedule ile pre-complete) | | **Transition `roles`** | **`GET .../functions/state` yanıtında `transitions[]` listesini** role gore **filtreleme** (v0.0.39+); transition yürütme (PATCH) için aynı listedeki deny'in zorunlu red anlamı **yoktur** | move-to-processing, complete-workflow; xUnit: `StateFunction_WithTestOperatorRole_*` / `StateFunction_WithTestViewerRole_*` | @@ -156,7 +156,7 @@ lifecycle-transitions-test-workflow (F) | Cancel | cancel-workflow → terminated-state | | **Exit** | initialize-state'ten exit-workflow; terminated-state ve `exitExecuted` | | **Schedule cancel** | auto-passed-state'te cancel-schedule-manually → pre-complete-state | -| **Reschedule** | reschedule-timer ile kisa sure `auto-passed-state`; yeniden kurulan ~10s timer sonrasi `timer-triggered-state` → auto → **pre-complete-state** (xUnit: `RescheduleTimer_SelfTransition_ThenWaitsForRescheduledTimer_ReachesPreCompleteState`) | +| **Reschedule** | reschedule-timer ile kisa sure `auto-passed-state`; yeniden kurulan ~6s timer sonrasi `timer-triggered-state` → auto → **pre-complete-state** (xUnit: `RescheduleTimer_SelfTransition_ThenWaitsForRescheduledTimer_ReachesPreCompleteState`) | | **QueryRoles / transition listesi** | **authorize:** `/functions/authorize?queryRoles=true&role=...` (datasi sorgu). **State listesi:** `GET .../functions/state` + role header ile **move-to-processing** gorunurlugu (allow/omit). **pre-complete-state:** cancel-schedule ile; queryRoles'ta test-processor allow / test-viewer deny | ### Kullanilan Bilesenler @@ -324,10 +324,10 @@ subflow-orchestration-parent (F) **Workflow'lar:** - `task-execution-test-workflow` (type: F) — Ana test workflow'u (HTTP, Script, StartFlow, GetInstanceData, Notification, TriggerTransition, SubProcess, GetInstances, Human task + manuel onay) -- `task-target-workflow` (type: F) — Cross-workflow / StartFlow / GetInstanceData / TriggerTransition / SubProcess / GetInstances icin hedef +- `task-target-workflow` (type: F) — StartFlow / GetInstanceData / TriggerTransition / SubProcess / GetInstances icin hedef - `extended-tasks-test-workflow` (type: F) — **Sadece Dapr** ile dis servis cagrisi: HTTP invoke, Service, Binding, PubSub (tek zincir; HTTP test: `api-tests/task-execution/task-execution.http` **Bolum 3**) -**Amac:** Tum **runtime-ici** gorev tipleri ile **Dapr tabanli** gorev tiplerini ayri workflow'larda dogrular; task siralama (`onEntries` order), `context.Body.data`, cross-workflow iletisim ve Dapr bilesen YAML (`etc/dapr/components/`) ile uyumu kapsar. +**Amac:** Tum **runtime-ici** gorev tipleri ile **Dapr tabanli** gorev tiplerini ayri workflow'larda dogrular; task siralama (`onEntries` order), `context.Body.data`, scheduled transition (Timer Task tip 9), auto transition (Condition Task tip 8) ve Dapr bilesen YAML (`etc/dapr/components/`) ile uyumu kapsar. **CSX klasorleri:** `Workflows/task-execution/src/task-execution-test-workflow/` (`task-execution-test-workflow.json` mapping'leri), `.../src/extended-tasks-test-workflow/` (`extended-tasks-test-workflow.json`), `.../src/shared/AlwaysTrueRule.csx` (otomatik gecis kurallari; `task-target-workflow` dahil). @@ -348,11 +348,15 @@ task-execution-test-workflow (F) │ ├── script-processing-state │ ├── onEntries: task-exec-script-task (type:7) + ScriptProcessMapping.csx -│ └── Transitions: auto-to-cross-workflow → cross-workflow-state +│ └── Transitions: auto-to-timer-wait → timer-wait-state │ -├── cross-workflow-state -│ ├── onEntries: task-exec-script-task + CrossWorkflowMapping.csx -│ └── Transitions: auto-to-start-flow → start-flow-state +├── timer-wait-state (Timer Task tip 9 testi) +│ ├── onEntries: task-exec-script-task + TimerStartMapping.csx +│ │ (timerStartedAt, timerExpectedSeconds=3 yazar) +│ └── Transitions: scheduled-to-start-flow → start-flow-state +│ (triggerType: 2 + ITimerMapping (TimerScheduleMapping.csx, 3 sn) → +│ runtime arka planda Timer Task uretir; testler timerStartedAt'tan +│ gecen sureye bakarak gercek beklemeyi dogrular) │ ├── start-flow-state │ ├── onEntries: start-flow-task (type:11) + StartFlowMapping.csx @@ -414,11 +418,13 @@ extended-tasks-test-workflow (F) |---------|-------------------|---------------| | HTTP Task (type:6) | Mocklab HTTP | http-process-task + HttpProcessMapping | | Script Task (type:7) | C# script data | task-exec-script-task + ScriptProcessMapping | +| **Condition Task (type:8)** | Sistem gorevi — `triggerType: 1` (auto) + `IConditionMapping` rule calistirilirken runtime tarafindan otomatik uretilir; manuel JSON ile tanimlanmaz. Tum `auto-to-*` gecislerinde `AlwaysTrueRule.csx` calisir → her auto transition icin Condition Task ortaya cikar. | Tum auto transition'lar (`script-processing-state`, `start-flow-state`, `get-instance-data-state`, `notification-state`, `trigger-transition-state`, `subprocess-state`, `get-instances-state`) + `shared/AlwaysTrueRule.csx` | +| **Timer Task (type:9)** | Sistem gorevi — `triggerType: 2` (scheduled) + `ITimerMapping` calistirilirken runtime tarafindan otomatik uretilir; manuel JSON ile tanimlanmaz. `timer-wait-state` cikis transition'i `scheduled-to-start-flow` 3 sn bekler; testler parent attributes.timerStartedAt ile (DateTime.UtcNow - timerStartedAt) >= ~2.5 sn olarak gercek beklemeyi dogrular. | timer-wait-state + TimerStartMapping (anlik mark) + TimerScheduleMapping (scheduled timer 3 sn) | | StartFlow Task (type:11) | Hedef workflow baslatma + hedef instance teyidi (`startedInstanceId` + GET state) | start-flow-task + StartFlowMapping (SetBody parentInstanceId/source/note) | | GetInstanceData Task (type:13) | Baska instance datasi | get-instance-data-task + GetInstanceDataMapping | | Notification Task (type:10) | Platform mapping G (script yok) | notification-task + mapping `{ "type": "G" }` | | TriggerTransition Task (type:12) | Hedef instance + manuel gecis | trigger-transition-task + TriggerTransitionMapping (SetInstance + `manual-complete-target`) | -| SubProcess Task (type:14) | Alt surec baslatma + hedef instance teyidi (`subprocessInstanceId` + GET state/attributes) | subprocess-task + SubProcessMapping (SetBody parentInstanceId/source/note) | +| SubProcess Task (type:14) | Alt surec baslatma + hedef instance teyidi (`subprocessInstanceId` + `subprocessData` yanit ozeti + GET state/attributes) | subprocess-task + SubProcessMapping (SetBody parentInstanceId/source/note; OutputHandler `subprocessData` = `context.Body.data` alanlari) | | GetInstances Task (type:15) | Instance listesi | get-instances-task + GetInstancesMapping | | Human Task (type:5) | Manuel `approve-human-task` | human-task + HumanTaskMapping | | Dapr HTTP (1) / Service (3) / Binding (2) / PubSub (4) | Sidecar + YAML | `extended-tasks-test-workflow` + dapr-*-task | @@ -426,7 +432,7 @@ extended-tasks-test-workflow (F) | context.Body.data | Task yaniti | OutputHandler'lar | | GetConfigValue | MocklabBaseUrl | HttpProcessMapping | | Task siralama | onEntries order | Tum state'ler | -| Cross-workflow | StartFlow + GetInstanceData + hedef workflow | task-target-workflow | +| Hedef workflow yasam dongusu | StartFlow + GetInstanceData + TriggerTransition + SubProcess hedefleri | task-target-workflow (ortak hedef) | ### Kullanilan Bilesenler (ozet) @@ -1113,7 +1119,9 @@ Her grubun hangi vNext ozelliklerini test ettigini gosteren matris. **Gn** sutun | Idempotent start | X | | | | | | X | | | | Instance filtreleme | | | X | | | | X | | | | Sayfalama/Siralama | | | X | | | | X | | | -| Cross-workflow task | | | X | | | | | | | +| Hedef workflow yasam dongusu (StartFlow/SubProcess hedefi) | | | X | | | | | | | +| **Condition Task (tip 8) — auto transition rule** | | | X | | | | | | | +| **Timer Task (tip 9) — scheduled transition** | | | X | | | | | | | | Complementary rules | X | | | | X | | | | | | **Dapr sidecar + bilesen YAML** | | | X | | | | | | | | **Flow tipi: Core (C)** | | | | | | | | X | | diff --git a/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs b/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs index e45342c..c89e4d0 100644 --- a/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs +++ b/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs @@ -54,7 +54,7 @@ await _workflow.RunTransitionAsync( await _workflow.WaitForStateAsync( instanceId, "pre-complete-state", - TimeSpan.FromSeconds(15) + TimeSpan.FromSeconds(10) ); await _workflow.RunTransitionAsync( @@ -100,7 +100,7 @@ await _workflow.RunTransitionAsync( await _workflow.WaitForStateAsync( instanceId, "pre-complete-state", - TimeSpan.FromSeconds(15) + TimeSpan.FromSeconds(10) ); var instanceAtPreComplete = await Api.GetInstanceAsync(WorkflowKey, instanceId); @@ -304,11 +304,11 @@ await _workflow.RunTransitionAsync( await _workflow.AssertStateAsync(instanceId, "pre-complete-state"); } - // TODO / BUG: reschedule-timer çalışınca workflow tanımına göre scheduled-timer-transition yeniden kurulmalı (~10s sonra timer-triggered → pre-complete). + // TODO / BUG: reschedule-timer çalışınca workflow tanımına göre scheduled-timer-transition yeniden kurulmalı (~6 sonra timer-triggered → pre-complete). // Şu an runtime’da timer yeniden schedule olmuyor; instance sürekli auto-passed-state’te kalıyor — WaitForStateAsync(pre-complete) zaman aşımına düşer. // Workflow JSON / reschedule-timer → auto-passed-state ($self) doğru kabul ediliyor; platform tarafı incelenmeli. /// - /// reschedule-timer: $self ile hâlâ auto-passed-state; ardından zamanlayıcı yeniden kurulmalı (ShortTimerMapping +10s). + /// reschedule-timer: $self ile hâlâ auto-passed-state; ardından zamanlayıcı yeniden kurulmalı (ShortTimerMapping +6). /// Beklenen: scheduled-timer-transition -> timer-triggered-state -> auto-to-pre-complete -> pre-complete-state. /// [Fact] @@ -336,7 +336,7 @@ await _workflow.RunTransitionAsync( await _workflow.AssertStateAsync(instanceId, "auto-passed-state"); - // Yeniden kurulan timer ~10 sn; timer-triggered onEntries + auto gecis icin pay + // Yeniden kurulan timer ~6 sn; timer-triggered onEntries + auto gecis icin pay await _workflow.WaitForStateAsync( instanceId, "pre-complete-state", diff --git a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs index 32d8d5d..138e1f9 100644 --- a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs +++ b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs @@ -14,10 +14,13 @@ internal static class TaskExecutionMainWorkflowInstanceDataAssertions "(task-execution-test-workflow mappings + task-execution.http B1 expected flags)"; /// - /// Human-task beklemeden önce: HTTP, script, cross-workflow, start-flow, get-instance-data, - /// trigger, subprocess, get-instances ve human-task onEntry mapping bayrakları. - /// Notification task mapping.type: G kullandığı için + /// Human-task beklemeden önce: HTTP, script, timer-wait (Timer Task tip 9), start-flow, + /// get-instance-data, trigger, subprocess, get-instances ve human-task onEntry mapping + /// bayrakları. Notification task mapping.type: G kullandığı için /// taskResults.notification entegrasyon assert'ine dahil edilmez. + /// timer-wait-state, scheduled transition (triggerType: 2) + ITimerMapping + /// ile 3 sn beklediği için timerStartedAt attribute'ü zorunludur ve test bunun + /// üzerinden gerçek bekleme süresini doğrular. /// public static void AssertWhileWaitingOnHumanTask(JsonElement attributes) { @@ -32,11 +35,18 @@ public static void AssertWhileWaitingOnHumanTask(JsonElement attributes) ContractHint ); JsonElementAssertions.AssertPropertyTrue(attributes, "scriptProcessed", ContractHint); - JsonElementAssertions.AssertPropertyTrue( + + JsonElementAssertions.AssertPropertyNonEmptyString( attributes, - "crossWorkflowCompleted", - ContractHint + "timerStartedAt", + $"timerStartedAt {ContractHint}" ); + Assert.True( + attributes.TryGetProperty("timerExpectedSeconds", out var expectedSeconds) + && expectedSeconds.ValueKind == JsonValueKind.Number, + $"timerExpectedSeconds should be a JSON number {ContractHint}" + ); + JsonElementAssertions.AssertPropertyTrue( attributes, "startFlowCompleted", @@ -72,6 +82,12 @@ public static void AssertWhileWaitingOnHumanTask(JsonElement attributes) "subprocessInstanceId", ContractHint ); + Assert.True( + attributes.TryGetProperty("subprocessData", out var subprocessData) + && subprocessData.ValueKind == JsonValueKind.Object, + $"subprocessData should be a JSON object (SubProcessTask response data snapshot) {ContractHint}" + ); + JsonElementAssertions.AssertNestedPropertyTrue( attributes, new[] { "taskResults", "getInstances" }, diff --git a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs index 7abcdc1..5fbdf9c 100644 --- a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs +++ b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs @@ -60,6 +60,25 @@ await _mainWorkflow.WaitForStateAsync( var attrsBefore = await GetAttributesAsync(MainWorkflowKey, instanceId); TaskExecutionMainWorkflowInstanceDataAssertions.AssertWhileWaitingOnHumanTask(attrsBefore); + // Timer Task (tip 9) gercekten beklemis mi? timer-wait-state'in scheduled transition'i + // ITimerMapping ile 3 sn sonra start-flow-state'e gecirir; eger sadece auto transition + // calissaydi human-task-state'e ~yari saniyede ulasirdik. timerStartedAt parent attributes'unda + // yazildigi icin (TimerStartMapping) human-task-state'e ulasilan ana kadar gecen sure 3 sn'den + // buyuk olmalidir. Toleransi 2.5 sn olarak aliyoruz; ust sinir koymuyoruz (CI yavasligi). + var timerStartedAtRaw = attrsBefore.GetProperty("timerStartedAt").GetString()!; + var timerStartedAt = DateTime.Parse( + timerStartedAtRaw, + System.Globalization.CultureInfo.InvariantCulture, + System.Globalization.DateTimeStyles.RoundtripKind + ); + var timerElapsed = DateTime.UtcNow - timerStartedAt; + Assert.True( + timerElapsed.TotalSeconds >= 2.5, + $"Timer Task (tip 9) should have delayed scheduled transition by ~3s; " + + $"elapsed since timerStartedAt={timerStartedAtRaw} is only {timerElapsed.TotalSeconds:F2}s. " + + "Eger bu deger 3 sn'den kucukse scheduled transition timer'i devre disi kalmis olabilir." + ); + // StartFlow + DirectTrigger zincirinin sadece parent instance'a `startedInstanceId` yazmış olması yeterli değildir. // Hedef workflow'un `functions/state` çağrısıyla; (1) StartTask yeni bir instance açtı, (2) DirectTriggerTask // hedefin manuel `manual-complete-target` geçişini gerçekten tetikledi, doğrulanır. From 79f3ad16ab149bb518d9b5d8cf2cd52026db33ba Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Thu, 30 Apr 2026 17:47:50 +0300 Subject: [PATCH 10/24] task execution is done but dapr tasks --- .../postman-task-execution-collection.json | 22 +---- api-tests/task-execution/task-execution.http | 25 ++---- core.link.json | 2 +- .../human-task.json | 17 ---- .../extended-tasks-test-workflow.diagram.json | 32 ++++++++ .../task-execution-test-workflow.diagram.json | 12 +-- .../GetInstancesMapping.csx | 1 - .../HumanTaskMapping.csx | 53 ------------ .../SubProcessMapping.csx | 1 - .../TriggerTransitionMapping.csx | 1 - .../task-execution-test-workflow.json | 68 ++-------------- doc/integration-test-documentation.md | 13 +-- tests/Core/Helpers/StateFunctionJson.cs | 79 ++++++++++++++++++ .../TaskExecutionInstanceDataAssertions.cs | 24 +++--- .../TaskExecutionTestWorkflowTests.cs | 81 +++++++++++-------- 15 files changed, 196 insertions(+), 235 deletions(-) delete mode 100644 core/Tasks/task-execution/task-execution-test-workflow/human-task.json create mode 100644 core/Workflows/task-execution/.meta/extended-tasks-test-workflow.diagram.json delete mode 100644 core/Workflows/task-execution/src/task-execution-test-workflow/HumanTaskMapping.csx diff --git a/api-tests/task-execution/postman-task-execution-collection.json b/api-tests/task-execution/postman-task-execution-collection.json index 0f15b1c..bf437d5 100644 --- a/api-tests/task-execution/postman-task-execution-collection.json +++ b/api-tests/task-execution/postman-task-execution-collection.json @@ -76,7 +76,7 @@ " var ts = Date.parse(timerStartedAt);", " pm.expect(isNaN(ts), \"timerStartedAt should be parseable ISO 8601\").to.equal(false);", " var elapsedMs = Date.now() - ts;", - " pm.expect(elapsedMs, \"sync start oncesinde human-task-state'e ulasilmasi 3 sn'lik scheduled transition'i ortaya cikarmali (>= 2500 ms)\").to.be.at.least(2500);", + " pm.expect(elapsedMs, \"sync start oncesinde completed-state'e ulasilmasi 3 sn'lik scheduled transition'i ortaya cikarmali (>= 2500 ms)\").to.be.at.least(2500);", " var expectedSeconds = (a && typeof a.timerExpectedSeconds === \"number\") ? a.timerExpectedSeconds : (typeof jsonData.timerExpectedSeconds === \"number\" ? jsonData.timerExpectedSeconds : null);", " pm.expect(expectedSeconds, \"timerExpectedSeconds attribute (TimerStartMapping)\").to.equal(3);", " var sfId = optPick(jsonData, a, [\"subflowInstanceId\", \"subFlowInstanceId\", \"childSubFlowInstanceId\"]);", @@ -94,7 +94,7 @@ ] }, { - "name": "Adim 2: Durum (human-task-state)", + "name": "Adim 2: Durum (completed-state)", "request": { "method": "GET", "header": [], @@ -271,21 +271,7 @@ "response": [] }, { - "name": "Adim 4: approve-human-task", - "request": { - "method": "PATCH", - "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/transitions/approve-human-task", - "host": ["{{baseUrl}}"], - "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{taskExecInstanceId}}", "transitions", "approve-human-task"] - }, - "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } - }, - "response": [] - }, - { - "name": "Adim 5: Durum (completed-state)", + "name": "Adim 4: Final durum (completed-state)", "request": { "method": "GET", "header": [], @@ -298,7 +284,7 @@ "response": [] }, { - "name": "Adim 6: Final instance data", + "name": "Adim 5: Final instance data", "request": { "method": "GET", "header": [], diff --git a/api-tests/task-execution/task-execution.http b/api-tests/task-execution/task-execution.http index 5d3c6ac..665b151 100644 --- a/api-tests/task-execution/task-execution.http +++ b/api-tests/task-execution/task-execution.http @@ -19,12 +19,14 @@ # HTTP -> Script -> Timer Wait (Timer Task tip 9, scheduled transition 3 sn) # -> Start Flow (11) -> Get Instance Data (13) # -> Notification (10, mapping.type G) -> TriggerTransition (12) -> SubProcess (14) -# -> GetInstances (15) -> Human Task (5) -> PATCH approve -> Completed +# -> GetInstances (15) -> Completed (auto) # Condition Task (tip 8) tum auto transition rule'larinda (AlwaysTrueRule) dolayli olarak uretilir. +# NOT: Human Task (tip 5) runtime tarafindan kaldirilacak gecici bir ozellik oldugu icin bu +# zincirden cikartildi; happy path manuel onay olmadan dogrudan completed-state'e ulasir. # Requires: MocklabBaseUrl config, Mocklab seeded (POST /api/test/process) ############################################################################### -### B1 — Adim 1: Ana workflow baslat (sync zincir human-task-state'te durur) +### B1 — Adim 1: Ana workflow baslat (sync zincir completed-state'e ulasir) # @name createTaskExecInstance POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json @@ -37,7 +39,7 @@ Content-Type: application/json @taskExecInstanceId = {{createTaskExecInstance.response.body.$.id}} -### B1 — Adim 2: Durum kontrol (human-task-state beklenir) +### B1 — Adim 2: Durum kontrol (completed-state, status: C) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/state ### B1 — Adim 3: Instance data (notification / trigger / subprocess / getInstances bayraklari) @@ -48,9 +50,8 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/ # subprocessInstanceId (SubProcessTask sonucu hedef workflow id), # subprocessData (SubProcessTask context.Body.data anlık kopyası: id/instanceId/state vb.), # getInstanceDataCompleted, taskResults.triggerTransition.completed, -# taskResults.subprocess.completed, taskResults.getInstances.completed, -# taskResults.humanTask.completed -# Not: human-task-state'e ulasilana kadar gecen toplam sure >= 3 sn olmali; aksi halde +# taskResults.subprocess.completed, taskResults.getInstances.completed +# Not: completed-state'e ulasilana kadar gecen toplam sure >= 3 sn olmali; aksi halde # scheduled transition'in Timer Task'i devre disi olabilir. GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/data @@ -70,18 +71,6 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/inst # note alani SubProcessMapping InputHandler'da SetBody ile yazilmali. GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{taskExecSubprocessId}} -### B1 — Adim 4: Human task onayla (manuel transition) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/transitions/approve-human-task -Content-Type: application/json - -{} - -### B1 — Adim 5: Durum kontrol (completed-state) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/state - -### B1 — Adim 6: Final instance data -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/data - ############################################################################### # BOLUM 2 — Happy path: Hedef workflow (task-target-workflow) bagimsiz baslatma ############################################################################### diff --git a/core.link.json b/core.link.json index 35c50c1..8b9906a 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-04-30T13:51:28.126Z" + "importedAt": "2026-04-30T14:29:58.117Z" } \ No newline at end of file diff --git a/core/Tasks/task-execution/task-execution-test-workflow/human-task.json b/core/Tasks/task-execution/task-execution-test-workflow/human-task.json deleted file mode 100644 index 792a823..0000000 --- a/core/Tasks/task-execution/task-execution-test-workflow/human-task.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "key": "human-task", - "version": "1.0.0", - "domain": "core", - "flow": "sys-tasks", - "flowVersion": "1.0.0", - "tags": ["integration-test", "task-execution", "human-task"], - "_comment": "TaskType 5 = Human Task (task-definition.schema.json). Script Task (7) degildir.", - "attributes": { - "type": "5", - "config": { - "title": "Task Execution Integration — Human Onay", - "instructions": "Integration test human-task-state adiminda manuel onay beklenir; PATCH approve-human-task ile tamamlanir.", - "assignedTo": "test-user" - } - } -} diff --git a/core/Workflows/task-execution/.meta/extended-tasks-test-workflow.diagram.json b/core/Workflows/task-execution/.meta/extended-tasks-test-workflow.diagram.json new file mode 100644 index 0000000..375c53f --- /dev/null +++ b/core/Workflows/task-execution/.meta/extended-tasks-test-workflow.diagram.json @@ -0,0 +1,32 @@ +{ + "nodePos": { + "__start__": { + "x": 270, + "y": -30 + }, + "init-state": { + "x": 380, + "y": 220 + }, + "dapr-http-state": { + "x": 650, + "y": -140 + }, + "dapr-service-state": { + "x": 880, + "y": 260 + }, + "dapr-binding-state": { + "x": 1230, + "y": -610 + }, + "dapr-pubsub-state": { + "x": 1340, + "y": 230 + }, + "completed-state": { + "x": 1680, + "y": -80 + } + } +} \ No newline at end of file diff --git a/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json b/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json index add8d23..e3de053 100644 --- a/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json +++ b/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json @@ -17,8 +17,8 @@ "y": 40 }, "get-instance-data-state": { - "x": 1750, - "y": 350 + "x": 1760, + "y": 370 }, "completed-state": { "x": 3110, @@ -28,10 +28,6 @@ "x": 540, "y": 60 }, - "human-task-state": { - "x": 2840, - "y": -10 - }, "get-instances-state": { "x": 2680, "y": 360 @@ -41,8 +37,8 @@ "y": -10 }, "trigger-transition-state": { - "x": 2180, - "y": 380 + "x": 2220, + "y": 360 }, "notification-state": { "x": 2000, diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstancesMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstancesMapping.csx index 30bad3c..8bfc087 100644 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstancesMapping.csx +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/GetInstancesMapping.csx @@ -40,7 +40,6 @@ public class GetInstancesMapping : ScriptBase, IMapping if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; - if (HasProperty(data.taskResults, "humanTask")) result.taskResults.humanTask = data.taskResults.humanTask; } result.taskResults.getInstances = new ExpandoObject(); diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/HumanTaskMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/HumanTaskMapping.csx deleted file mode 100644 index 5a65b73..0000000 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/HumanTaskMapping.csx +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Dynamic; -using System.Threading.Tasks; -using BBT.Workflow.Scripting; -using BBT.Workflow.Definitions; - -public class HumanTaskMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public Task OutputHandler(ScriptContext context) - { - var data = context.Instance.Data; - dynamic result = new ExpandoObject(); - - if (HasProperty(data, "testId")) result.testId = data.testId; - if (HasProperty(data, "startedAt")) result.startedAt = data.startedAt; - if (HasProperty(data, "httpTaskCompleted")) result.httpTaskCompleted = data.httpTaskCompleted; - if (HasProperty(data, "httpStatusCode")) result.httpStatusCode = data.httpStatusCode; - if (HasProperty(data, "httpIsSuccess")) result.httpIsSuccess = data.httpIsSuccess; - if (HasProperty(data, "processId")) result.processId = data.processId; - if (HasProperty(data, "scriptProcessed")) result.scriptProcessed = data.scriptProcessed; - if (HasProperty(data, "scriptProcessedAt")) result.scriptProcessedAt = data.scriptProcessedAt; - if (HasProperty(data, "timerStartedAt")) result.timerStartedAt = data.timerStartedAt; - if (HasProperty(data, "timerExpectedSeconds")) result.timerExpectedSeconds = data.timerExpectedSeconds; - if (HasProperty(data, "startFlowCompleted")) result.startFlowCompleted = data.startFlowCompleted; - if (HasProperty(data, "startFlowIsSuccess")) result.startFlowIsSuccess = data.startFlowIsSuccess; - if (HasProperty(data, "startedInstanceId")) result.startedInstanceId = data.startedInstanceId; - if (HasProperty(data, "getInstanceDataCompleted")) result.getInstanceDataCompleted = data.getInstanceDataCompleted; - if (HasProperty(data, "getInstanceDataIsSuccess")) result.getInstanceDataIsSuccess = data.getInstanceDataIsSuccess; - if (HasProperty(data, "remoteInstanceData")) result.remoteInstanceData = data.remoteInstanceData; - - result.taskResults = new ExpandoObject(); - if (HasProperty(data, "taskResults")) - { - if (HasProperty(data.taskResults, "notification")) result.taskResults.notification = data.taskResults.notification; - if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; - if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; - if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; - if (HasProperty(data.taskResults, "humanTask")) result.taskResults.humanTask = data.taskResults.humanTask; - } - - result.taskResults.humanTask = new ExpandoObject(); - result.taskResults.humanTask.completed = true; - result.taskResults.humanTask.approvedAt = DateTime.UtcNow.ToString("o"); - - LogInformation("HumanTaskMapping completed"); - return Task.FromResult(new ScriptResponse { Data = result }); - } -} diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx index 237b4b5..e5c10dc 100644 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/SubProcessMapping.csx @@ -58,7 +58,6 @@ public class SubProcessMapping : ScriptBase, IMapping if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; - if (HasProperty(data.taskResults, "humanTask")) result.taskResults.humanTask = data.taskResults.humanTask; } result.taskResults.subprocess = new ExpandoObject(); diff --git a/core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx b/core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx index abc85d0..1c5772e 100644 --- a/core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx +++ b/core/Workflows/task-execution/src/task-execution-test-workflow/TriggerTransitionMapping.csx @@ -51,7 +51,6 @@ public class TriggerTransitionMapping : ScriptBase, IMapping if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; - if (HasProperty(data.taskResults, "humanTask")) result.taskResults.humanTask = data.taskResults.humanTask; } result.taskResults.triggerTransition = new ExpandoObject(); diff --git a/core/Workflows/task-execution/task-execution-test-workflow.json b/core/Workflows/task-execution/task-execution-test-workflow.json index f82cc39..b2d12e1 100644 --- a/core/Workflows/task-execution/task-execution-test-workflow.json +++ b/core/Workflows/task-execution/task-execution-test-workflow.json @@ -15,7 +15,6 @@ "trigger-transition-task", "subprocess-task", "get-instances-task", - "human-task", "task-ordering", "on-entries" ], @@ -437,7 +436,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/TriggerTransitionMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBUcmlnZ2VyVHJhbnNpdGlvbk1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRpcmVjdFRyaWdnZXIgPSAodGFzayBhcyBEaXJlY3RUcmlnZ2VyVGFzaykhOwogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRJbnN0YW5jZUlkIikpCiAgICAgICAgewogICAgICAgICAgICB2YXIgdGFyZ2V0SWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkPy5Ub1N0cmluZygpOwogICAgICAgICAgICBkaXJlY3RUcmlnZ2VyLlNldEluc3RhbmNlKHRhcmdldElkISk7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCQiVHJpZ2dlclRyYW5zaXRpb25NYXBwaW5nIElucHV0SGFuZGxlcjogU2V0SW5zdGFuY2Uge3RhcmdldElkfSIpOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRpbWVyU3RhcnRlZEF0IikpIHJlc3VsdC50aW1lclN0YXJ0ZWRBdCA9IGRhdGEudGltZXJTdGFydGVkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0aW1lckV4cGVjdGVkU2Vjb25kcyIpKSByZXN1bHQudGltZXJFeHBlY3RlZFNlY29uZHMgPSBkYXRhLnRpbWVyRXhwZWN0ZWRTZWNvbmRzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJodW1hblRhc2siKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IGRhdGEudGFza1Jlc3VsdHMuaHVtYW5UYXNrOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24uY29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24uZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiVHJpZ2dlclRyYW5zaXRpb25NYXBwaW5nIGNvbXBsZXRlZCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBUcmlnZ2VyVHJhbnNpdGlvbk1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRpcmVjdFRyaWdnZXIgPSAodGFzayBhcyBEaXJlY3RUcmlnZ2VyVGFzaykhOwogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRJbnN0YW5jZUlkIikpCiAgICAgICAgewogICAgICAgICAgICB2YXIgdGFyZ2V0SWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkPy5Ub1N0cmluZygpOwogICAgICAgICAgICBkaXJlY3RUcmlnZ2VyLlNldEluc3RhbmNlKHRhcmdldElkISk7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCQiVHJpZ2dlclRyYW5zaXRpb25NYXBwaW5nIElucHV0SGFuZGxlcjogU2V0SW5zdGFuY2Uge3RhcmdldElkfSIpOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRpbWVyU3RhcnRlZEF0IikpIHJlc3VsdC50aW1lclN0YXJ0ZWRBdCA9IGRhdGEudGltZXJTdGFydGVkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0aW1lckV4cGVjdGVkU2Vjb25kcyIpKSByZXN1bHQudGltZXJFeHBlY3RlZFNlY29uZHMgPSBkYXRhLnRpbWVyRXhwZWN0ZWRTZWNvbmRzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICB9CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uLmNvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uLmV4ZWN1dGVkQXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlRyaWdnZXJUcmFuc2l0aW9uTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } ], @@ -494,7 +493,7 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/SubProcessMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBTdWJQcm9jZXNzTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICAvLyBTdWJQcm9jZXNzVGFzayAodGlwIDE0KSBmaXJlLWFuZC1mb3JnZXQgYmlyIGFsdCBpbnN0YW5jZSBhY2FyLgogICAgICAgIC8vIEJvZHkgdXplcmluZGVuIHN1YnByb2Nlc3MnaW4gaW5zdGFuY2UgZGF0YSdzaW5hIHBhcmVudCByZWZlcmFuc2kgdmUga2F5bmFrIG5vdHUgeWF6aXlvcnV6OwogICAgICAgIC8vIHRlc3RsZXIgYnUgYWxhbmxhcmkgc3VicHJvY2VzcyBHZXRJbnN0YW5jZSBhdHRyaWJ1dGVzJ3VuZGEgZG9ncnVsYXlhYmlsc2luLgogICAgICAgIHZhciBzdWJQcm9jZXNzVGFzayA9ICh0YXNrIGFzIFN1YlByb2Nlc3NUYXNrKSE7CgogICAgICAgIHZhciBib2R5ID0gbmV3CiAgICAgICAgewogICAgICAgICAgICBzb3VyY2UgPSAidGFzay1leGVjdXRpb24tdGVzdCIsCiAgICAgICAgICAgIHBhcmVudEluc3RhbmNlSWQgPSBjb250ZXh0Lkluc3RhbmNlLklkLAogICAgICAgICAgICBub3RlID0gInRoaXMgaXMgYSBzdWJwcm9jZXNzIHN0YXJ0ZWQgZnJvbSB0YXNrLWV4ZWN1dGlvbi10ZXN0LXdvcmtmbG93IgogICAgICAgIH07CiAgICAgICAgc3ViUHJvY2Vzc1Rhc2suU2V0Qm9keShib2R5KTsKCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlN1YlByb2Nlc3NNYXBwaW5nIElucHV0SGFuZGxlcjogYm9keSBzZXQgd2l0aCBwYXJlbnRJbnN0YW5jZUlkL3NvdXJjZS9ub3RlIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIHZhciB0YXNrUmVzcG9uc2UgPSBjb250ZXh0LkJvZHk7CiAgICAgICAgdmFyIHJlc3BvbnNlQm9keSA9IHRhc2tSZXNwb25zZT8uZGF0YTsKCiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkQXQiKSkgcmVzdWx0LnN0YXJ0ZWRBdCA9IGRhdGEuc3RhcnRlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBTdGF0dXNDb2RlIikpIHJlc3VsdC5odHRwU3RhdHVzQ29kZSA9IGRhdGEuaHR0cFN0YXR1c0NvZGU7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwSXNTdWNjZXNzIikpIHJlc3VsdC5odHRwSXNTdWNjZXNzID0gZGF0YS5odHRwSXNTdWNjZXNzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicHJvY2Vzc0lkIikpIHJlc3VsdC5wcm9jZXNzSWQgPSBkYXRhLnByb2Nlc3NJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInNjcmlwdFByb2Nlc3NlZCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkID0gZGF0YS5zY3JpcHRQcm9jZXNzZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWRBdCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkQXQgPSBkYXRhLnNjcmlwdFByb2Nlc3NlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGltZXJTdGFydGVkQXQiKSkgcmVzdWx0LnRpbWVyU3RhcnRlZEF0ID0gZGF0YS50aW1lclN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRpbWVyRXhwZWN0ZWRTZWNvbmRzIikpIHJlc3VsdC50aW1lckV4cGVjdGVkU2Vjb25kcyA9IGRhdGEudGltZXJFeHBlY3RlZFNlY29uZHM7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydEZsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LnN0YXJ0Rmxvd0NvbXBsZXRlZCA9IGRhdGEuc3RhcnRGbG93Q29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93SXNTdWNjZXNzIikpIHJlc3VsdC5zdGFydEZsb3dJc1N1Y2Nlc3MgPSBkYXRhLnN0YXJ0Rmxvd0lzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRJbnN0YW5jZUlkIikpIHJlc3VsdC5zdGFydGVkSW5zdGFuY2VJZCA9IGRhdGEuc3RhcnRlZEluc3RhbmNlSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJnZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQiKSkgcmVzdWx0LmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZCA9IGRhdGEuZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3MgPSBkYXRhLmdldEluc3RhbmNlRGF0YUlzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInJlbW90ZUluc3RhbmNlRGF0YSIpKSByZXN1bHQucmVtb3RlSW5zdGFuY2VEYXRhID0gZGF0YS5yZW1vdGVJbnN0YW5jZURhdGE7CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0YXNrUmVzdWx0cyIpKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInRyaWdnZXJUcmFuc2l0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAic3VicHJvY2VzcyIpKSByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2VzcyA9IGRhdGEudGFza1Jlc3VsdHMuc3VicHJvY2VzczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJnZXRJbnN0YW5jZXMiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IGRhdGEudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImh1bWFuVGFzayIpKSByZXN1bHQudGFza1Jlc3VsdHMuaHVtYW5UYXNrID0gZGF0YS50YXNrUmVzdWx0cy5odW1hblRhc2s7CiAgICAgICAgfQoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2VzcyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MuY29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2Vzcy5leGVjdXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIC8vIFN1YlByb2Nlc3NUYXNrIHlhbml0aW5pbiBnZXJjZWsgc2VydmlzICJkYXRhIiBnb3ZkZXNpIHBhcmVudCBhdHRyaWJ1dGVzLnN1YnByb2Nlc3NEYXRhIGFsdGluZGEgc2FrbGFuaXIKICAgICAgICAvLyAoaW50ZWdyYXRpb24gdGVzdCB2ZSBvcGVyYXN5b25lbCBpbmNlbGVtZSBpY2luKS4gQXlyaWNhIHN1YnByb2Nlc3NJbnN0YW5jZUlkIGtvbGF5IGVyaXNpbSBpY2luIGF5cmkgeWF6aWxpci4KICAgICAgICBpZiAocmVzcG9uc2VCb2R5ICE9IG51bGwpCiAgICAgICAgewogICAgICAgICAgICBkeW5hbWljIHNuYXBzaG90ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgImlkIikpIHNuYXBzaG90LmlkID0gcmVzcG9uc2VCb2R5LmlkOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkocmVzcG9uc2VCb2R5LCAiaW5zdGFuY2VJZCIpKSBzbmFwc2hvdC5pbnN0YW5jZUlkID0gcmVzcG9uc2VCb2R5Lmluc3RhbmNlSWQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShyZXNwb25zZUJvZHksICJzdGF0ZSIpKSBzbmFwc2hvdC5zdGF0ZSA9IHJlc3BvbnNlQm9keS5zdGF0ZTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgImxhdW5jaGVkIikpIHNuYXBzaG90LmxhdW5jaGVkID0gcmVzcG9uc2VCb2R5LmxhdW5jaGVkOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkocmVzcG9uc2VCb2R5LCAic3RhdHVzIikpIHNuYXBzaG90LnN0YXR1cyA9IHJlc3BvbnNlQm9keS5zdGF0dXM7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShyZXNwb25zZUJvZHksICJzdGF0dXNDb2RlIikpIHNuYXBzaG90LnN0YXR1c0NvZGUgPSByZXNwb25zZUJvZHkuc3RhdHVzQ29kZTsKICAgICAgICAgICAgcmVzdWx0LnN1YnByb2Nlc3NEYXRhID0gc25hcHNob3Q7CgogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkocmVzcG9uc2VCb2R5LCAiaWQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5zdWJwcm9jZXNzSW5zdGFuY2VJZCA9IHJlc3BvbnNlQm9keS5pZDsKICAgICAgICAgICAgZWxzZSBpZiAoSGFzUHJvcGVydHkocmVzcG9uc2VCb2R5LCAiaW5zdGFuY2VJZCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnN1YnByb2Nlc3NJbnN0YW5jZUlkID0gcmVzcG9uc2VCb2R5Lmluc3RhbmNlSWQ7CiAgICAgICAgfQoKICAgICAgICBpZiAodGFza1Jlc3BvbnNlICE9IG51bGwgJiYgSGFzUHJvcGVydHkodGFza1Jlc3BvbnNlLCAiaXNTdWNjZXNzIikpCiAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzLmlzU3VjY2VzcyA9IHRhc2tSZXNwb25zZS5pc1N1Y2Nlc3M7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCJTdWJQcm9jZXNzTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBTdWJQcm9jZXNzTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICAvLyBTdWJQcm9jZXNzVGFzayAodGlwIDE0KSBmaXJlLWFuZC1mb3JnZXQgYmlyIGFsdCBpbnN0YW5jZSBhY2FyLgogICAgICAgIC8vIEJvZHkgdXplcmluZGVuIHN1YnByb2Nlc3MnaW4gaW5zdGFuY2UgZGF0YSdzaW5hIHBhcmVudCByZWZlcmFuc2kgdmUga2F5bmFrIG5vdHUgeWF6aXlvcnV6OwogICAgICAgIC8vIHRlc3RsZXIgYnUgYWxhbmxhcmkgc3VicHJvY2VzcyBHZXRJbnN0YW5jZSBhdHRyaWJ1dGVzJ3VuZGEgZG9ncnVsYXlhYmlsc2luLgogICAgICAgIHZhciBzdWJQcm9jZXNzVGFzayA9ICh0YXNrIGFzIFN1YlByb2Nlc3NUYXNrKSE7CgogICAgICAgIHZhciBib2R5ID0gbmV3CiAgICAgICAgewogICAgICAgICAgICBzb3VyY2UgPSAidGFzay1leGVjdXRpb24tdGVzdCIsCiAgICAgICAgICAgIHBhcmVudEluc3RhbmNlSWQgPSBjb250ZXh0Lkluc3RhbmNlLklkLAogICAgICAgICAgICBub3RlID0gInRoaXMgaXMgYSBzdWJwcm9jZXNzIHN0YXJ0ZWQgZnJvbSB0YXNrLWV4ZWN1dGlvbi10ZXN0LXdvcmtmbG93IgogICAgICAgIH07CiAgICAgICAgc3ViUHJvY2Vzc1Rhc2suU2V0Qm9keShib2R5KTsKCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlN1YlByb2Nlc3NNYXBwaW5nIElucHV0SGFuZGxlcjogYm9keSBzZXQgd2l0aCBwYXJlbnRJbnN0YW5jZUlkL3NvdXJjZS9ub3RlIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIHZhciB0YXNrUmVzcG9uc2UgPSBjb250ZXh0LkJvZHk7CiAgICAgICAgdmFyIHJlc3BvbnNlQm9keSA9IHRhc2tSZXNwb25zZT8uZGF0YTsKCiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkQXQiKSkgcmVzdWx0LnN0YXJ0ZWRBdCA9IGRhdGEuc3RhcnRlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cFRhc2tDb21wbGV0ZWQiKSkgcmVzdWx0Lmh0dHBUYXNrQ29tcGxldGVkID0gZGF0YS5odHRwVGFza0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBTdGF0dXNDb2RlIikpIHJlc3VsdC5odHRwU3RhdHVzQ29kZSA9IGRhdGEuaHR0cFN0YXR1c0NvZGU7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwSXNTdWNjZXNzIikpIHJlc3VsdC5odHRwSXNTdWNjZXNzID0gZGF0YS5odHRwSXNTdWNjZXNzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicHJvY2Vzc0lkIikpIHJlc3VsdC5wcm9jZXNzSWQgPSBkYXRhLnByb2Nlc3NJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInNjcmlwdFByb2Nlc3NlZCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkID0gZGF0YS5zY3JpcHRQcm9jZXNzZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWRBdCIpKSByZXN1bHQuc2NyaXB0UHJvY2Vzc2VkQXQgPSBkYXRhLnNjcmlwdFByb2Nlc3NlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGltZXJTdGFydGVkQXQiKSkgcmVzdWx0LnRpbWVyU3RhcnRlZEF0ID0gZGF0YS50aW1lclN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRpbWVyRXhwZWN0ZWRTZWNvbmRzIikpIHJlc3VsdC50aW1lckV4cGVjdGVkU2Vjb25kcyA9IGRhdGEudGltZXJFeHBlY3RlZFNlY29uZHM7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydEZsb3dDb21wbGV0ZWQiKSkgcmVzdWx0LnN0YXJ0Rmxvd0NvbXBsZXRlZCA9IGRhdGEuc3RhcnRGbG93Q29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93SXNTdWNjZXNzIikpIHJlc3VsdC5zdGFydEZsb3dJc1N1Y2Nlc3MgPSBkYXRhLnN0YXJ0Rmxvd0lzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRJbnN0YW5jZUlkIikpIHJlc3VsdC5zdGFydGVkSW5zdGFuY2VJZCA9IGRhdGEuc3RhcnRlZEluc3RhbmNlSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJnZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQiKSkgcmVzdWx0LmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZCA9IGRhdGEuZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3MgPSBkYXRhLmdldEluc3RhbmNlRGF0YUlzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInJlbW90ZUluc3RhbmNlRGF0YSIpKSByZXN1bHQucmVtb3RlSW5zdGFuY2VEYXRhID0gZGF0YS5yZW1vdGVJbnN0YW5jZURhdGE7CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0YXNrUmVzdWx0cyIpKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInRyaWdnZXJUcmFuc2l0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAic3VicHJvY2VzcyIpKSByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2VzcyA9IGRhdGEudGFza1Jlc3VsdHMuc3VicHJvY2VzczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJnZXRJbnN0YW5jZXMiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IGRhdGEudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzLmNvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MuZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICAvLyBTdWJQcm9jZXNzVGFzayB5YW5pdGluaW4gZ2VyY2VrIHNlcnZpcyAiZGF0YSIgZ292ZGVzaSBwYXJlbnQgYXR0cmlidXRlcy5zdWJwcm9jZXNzRGF0YSBhbHRpbmRhIHNha2xhbmlyCiAgICAgICAgLy8gKGludGVncmF0aW9uIHRlc3QgdmUgb3BlcmFzeW9uZWwgaW5jZWxlbWUgaWNpbikuIEF5cmljYSBzdWJwcm9jZXNzSW5zdGFuY2VJZCBrb2xheSBlcmlzaW0gaWNpbiBheXJpIHlhemlsaXIuCiAgICAgICAgaWYgKHJlc3BvbnNlQm9keSAhPSBudWxsKQogICAgICAgIHsKICAgICAgICAgICAgZHluYW1pYyBzbmFwc2hvdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShyZXNwb25zZUJvZHksICJpZCIpKSBzbmFwc2hvdC5pZCA9IHJlc3BvbnNlQm9keS5pZDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgImluc3RhbmNlSWQiKSkgc25hcHNob3QuaW5zdGFuY2VJZCA9IHJlc3BvbnNlQm9keS5pbnN0YW5jZUlkOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkocmVzcG9uc2VCb2R5LCAic3RhdGUiKSkgc25hcHNob3Quc3RhdGUgPSByZXNwb25zZUJvZHkuc3RhdGU7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShyZXNwb25zZUJvZHksICJsYXVuY2hlZCIpKSBzbmFwc2hvdC5sYXVuY2hlZCA9IHJlc3BvbnNlQm9keS5sYXVuY2hlZDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgInN0YXR1cyIpKSBzbmFwc2hvdC5zdGF0dXMgPSByZXNwb25zZUJvZHkuc3RhdHVzOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkocmVzcG9uc2VCb2R5LCAic3RhdHVzQ29kZSIpKSBzbmFwc2hvdC5zdGF0dXNDb2RlID0gcmVzcG9uc2VCb2R5LnN0YXR1c0NvZGU7CiAgICAgICAgICAgIHJlc3VsdC5zdWJwcm9jZXNzRGF0YSA9IHNuYXBzaG90OwoKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgImlkIikpCiAgICAgICAgICAgICAgICByZXN1bHQuc3VicHJvY2Vzc0luc3RhbmNlSWQgPSByZXNwb25zZUJvZHkuaWQ7CiAgICAgICAgICAgIGVsc2UgaWYgKEhhc1Byb3BlcnR5KHJlc3BvbnNlQm9keSwgImluc3RhbmNlSWQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5zdWJwcm9jZXNzSW5zdGFuY2VJZCA9IHJlc3BvbnNlQm9keS5pbnN0YW5jZUlkOwogICAgICAgIH0KCiAgICAgICAgaWYgKHRhc2tSZXNwb25zZSAhPSBudWxsICYmIEhhc1Byb3BlcnR5KHRhc2tSZXNwb25zZSwgImlzU3VjY2VzcyIpKQogICAgICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2Vzcy5pc1N1Y2Nlc3MgPSB0YXNrUmVzcG9uc2UuaXNTdWNjZXNzOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU3ViUHJvY2Vzc01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } ], @@ -551,25 +550,25 @@ }, "mapping": { "location": "./src/task-execution-test-workflow/GetInstancesMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEdldEluc3RhbmNlc01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRpbWVyU3RhcnRlZEF0IikpIHJlc3VsdC50aW1lclN0YXJ0ZWRBdCA9IGRhdGEudGltZXJTdGFydGVkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0aW1lckV4cGVjdGVkU2Vjb25kcyIpKSByZXN1bHQudGltZXJFeHBlY3RlZFNlY29uZHMgPSBkYXRhLnRpbWVyRXhwZWN0ZWRTZWNvbmRzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJodW1hblRhc2siKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IGRhdGEudGFza1Jlc3VsdHMuaHVtYW5UYXNrOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcy5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMuZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICB2YXIgdGFza1Jlc3BvbnNlID0gY29udGV4dC5Cb2R5OwogICAgICAgIGlmICh0YXNrUmVzcG9uc2UgIT0gbnVsbCAmJiBIYXNQcm9wZXJ0eSh0YXNrUmVzcG9uc2UsICJkYXRhIikpCiAgICAgICAgewogICAgICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzLnJlc3BvbnNlUmVjZWl2ZWQgPSB0cnVlOwogICAgICAgIH0KCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkdldEluc3RhbmNlc01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEdldEluc3RhbmNlc01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRpbWVyU3RhcnRlZEF0IikpIHJlc3VsdC50aW1lclN0YXJ0ZWRBdCA9IGRhdGEudGltZXJTdGFydGVkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0aW1lckV4cGVjdGVkU2Vjb25kcyIpKSByZXN1bHQudGltZXJFeHBlY3RlZFNlY29uZHMgPSBkYXRhLnRpbWVyRXhwZWN0ZWRTZWNvbmRzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICB9CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMuY29tcGxldGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzLmV4ZWN1dGVkQXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKCiAgICAgICAgdmFyIHRhc2tSZXNwb25zZSA9IGNvbnRleHQuQm9keTsKICAgICAgICBpZiAodGFza1Jlc3BvbnNlICE9IG51bGwgJiYgSGFzUHJvcGVydHkodGFza1Jlc3BvbnNlLCAiZGF0YSIpKQogICAgICAgIHsKICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcy5yZXNwb25zZVJlY2VpdmVkID0gdHJ1ZTsKICAgICAgICB9CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCJHZXRJbnN0YW5jZXNNYXBwaW5nIGNvbXBsZXRlZCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" } } ], "onExits": [], "transitions": [ { - "key": "auto-to-human-task", - "target": "human-task-state", + "key": "auto-to-completed", + "target": "completed-state", "triggerType": 1, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Auto to Human Task" + "label": "Auto to Completed" }, { "language": "tr-TR", - "label": "Insan Gorevine Otomatik" + "label": "Tamamlandi Durumuna Otomatik" } ], "rule": { @@ -580,59 +579,6 @@ } ] }, - { - "key": "human-task-state", - "stateType": 2, - "subType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Human Task (Approval)" - }, - { - "language": "tr-TR", - "label": "Insan Gorevi (Onay)" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - { - "order": 1, - "task": { - "key": "human-task", - "domain": "core", - "version": "1.0.0", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/task-execution-test-workflow/HumanTaskMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEh1bWFuVGFza01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImh0dHBUYXNrQ29tcGxldGVkIikpIHJlc3VsdC5odHRwVGFza0NvbXBsZXRlZCA9IGRhdGEuaHR0cFRhc2tDb21wbGV0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwU3RhdHVzQ29kZSIpKSByZXN1bHQuaHR0cFN0YXR1c0NvZGUgPSBkYXRhLmh0dHBTdGF0dXNDb2RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cElzU3VjY2VzcyIpKSByZXN1bHQuaHR0cElzU3VjY2VzcyA9IGRhdGEuaHR0cElzU3VjY2VzczsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByb2Nlc3NJZCIpKSByZXN1bHQucHJvY2Vzc0lkID0gZGF0YS5wcm9jZXNzSWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzY3JpcHRQcm9jZXNzZWQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZCA9IGRhdGEuc2NyaXB0UHJvY2Vzc2VkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic2NyaXB0UHJvY2Vzc2VkQXQiKSkgcmVzdWx0LnNjcmlwdFByb2Nlc3NlZEF0ID0gZGF0YS5zY3JpcHRQcm9jZXNzZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRpbWVyU3RhcnRlZEF0IikpIHJlc3VsdC50aW1lclN0YXJ0ZWRBdCA9IGRhdGEudGltZXJTdGFydGVkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0aW1lckV4cGVjdGVkU2Vjb25kcyIpKSByZXN1bHQudGltZXJFeHBlY3RlZFNlY29uZHMgPSBkYXRhLnRpbWVyRXhwZWN0ZWRTZWNvbmRzOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRGbG93Q29tcGxldGVkIikpIHJlc3VsdC5zdGFydEZsb3dDb21wbGV0ZWQgPSBkYXRhLnN0YXJ0Rmxvd0NvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0Rmxvd0lzU3VjY2VzcyIpKSByZXN1bHQuc3RhcnRGbG93SXNTdWNjZXNzID0gZGF0YS5zdGFydEZsb3dJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkSW5zdGFuY2VJZCIpKSByZXN1bHQuc3RhcnRlZEluc3RhbmNlSWQgPSBkYXRhLnN0YXJ0ZWRJbnN0YW5jZUlkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0SW5zdGFuY2VEYXRhQ29tcGxldGVkIikpIHJlc3VsdC5nZXRJbnN0YW5jZURhdGFDb21wbGV0ZWQgPSBkYXRhLmdldEluc3RhbmNlRGF0YUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldEluc3RhbmNlRGF0YUlzU3VjY2VzcyIpKSByZXN1bHQuZ2V0SW5zdGFuY2VEYXRhSXNTdWNjZXNzID0gZGF0YS5nZXRJbnN0YW5jZURhdGFJc1N1Y2Nlc3M7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZW1vdGVJbnN0YW5jZURhdGEiKSkgcmVzdWx0LnJlbW90ZUluc3RhbmNlRGF0YSA9IGRhdGEucmVtb3RlSW5zdGFuY2VEYXRhOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAibm90aWZpY2F0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJodW1hblRhc2siKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IGRhdGEudGFza1Jlc3VsdHMuaHVtYW5UYXNrOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzayA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmh1bWFuVGFzay5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5odW1hblRhc2suYXBwcm92ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiSHVtYW5UYXNrTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" - } - } - ], - "onExits": [], - "transitions": [ - { - "key": "approve-human-task", - "target": "completed-state", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Approve Human Task" - }, - { - "language": "tr-TR", - "label": "Insan Gorevini Onayla" - } - ], - "onExecutionTasks": [] - } - ] - }, { "key": "completed-state", "stateType": 3, diff --git a/doc/integration-test-documentation.md b/doc/integration-test-documentation.md index 1390dae..f9d44ce 100644 --- a/doc/integration-test-documentation.md +++ b/doc/integration-test-documentation.md @@ -323,7 +323,7 @@ subflow-orchestration-parent (F) ## Grup 3: Task Execution (runtime gorevleri + Dapr dis servis) **Workflow'lar:** -- `task-execution-test-workflow` (type: F) — Ana test workflow'u (HTTP, Script, StartFlow, GetInstanceData, Notification, TriggerTransition, SubProcess, GetInstances, Human task + manuel onay) +- `task-execution-test-workflow` (type: F) — Ana test workflow'u (HTTP, Script, Timer Wait, StartFlow, GetInstanceData, Notification, TriggerTransition, SubProcess, GetInstances → completed). Human Task (tip 5) runtime tarafindan kaldirilacak gecici bir ozellik oldugu icin bu zincirden cikartildi. - `task-target-workflow` (type: F) — StartFlow / GetInstanceData / TriggerTransition / SubProcess / GetInstances icin hedef - `extended-tasks-test-workflow` (type: F) — **Sadece Dapr** ile dis servis cagrisi: HTTP invoke, Service, Binding, PubSub (tek zincir; HTTP test: `api-tests/task-execution/task-execution.http` **Bolum 3**) @@ -380,11 +380,7 @@ task-execution-test-workflow (F) │ ├── get-instances-state │ ├── onEntries: get-instances-task (type:15) + GetInstancesMapping.csx -│ └── Transitions: auto-to-human-task → human-task-state -│ -├── human-task-state -│ ├── onEntries: human-task (type:5) + HumanTaskMapping.csx -│ └── Transitions: approve-human-task (Manual) → completed-state +│ └── Transitions: auto-to-completed → completed-state │ └── completed-state (Final/Success, stateType:3, subType:1) @@ -409,7 +405,7 @@ extended-tasks-test-workflow (F) ├── dapr-http-state → dapr-service-state → dapr-binding-state → dapr-pubsub-state │ (sirasiyla type 1, 3, 2, 4 Dapr task'leri + Dapr*Mapping.csx) │ -└── completed-state (Final; pubsub sonrasi otomatik tamamlanma, manuel human task yok) +└── completed-state (Final; pubsub sonrasi otomatik tamamlanma) ``` ### Test Edilen Ozellikler @@ -426,7 +422,6 @@ extended-tasks-test-workflow (F) | TriggerTransition Task (type:12) | Hedef instance + manuel gecis | trigger-transition-task + TriggerTransitionMapping (SetInstance + `manual-complete-target`) | | SubProcess Task (type:14) | Alt surec baslatma + hedef instance teyidi (`subprocessInstanceId` + `subprocessData` yanit ozeti + GET state/attributes) | subprocess-task + SubProcessMapping (SetBody parentInstanceId/source/note; OutputHandler `subprocessData` = `context.Body.data` alanlari) | | GetInstances Task (type:15) | Instance listesi | get-instances-task + GetInstancesMapping | -| Human Task (type:5) | Manuel `approve-human-task` | human-task + HumanTaskMapping | | Dapr HTTP (1) / Service (3) / Binding (2) / PubSub (4) | Sidecar + YAML | `extended-tasks-test-workflow` + dapr-*-task | | HttpTask / StartTask / GetInstanceDataTask cast | InputHandler | Ilgili mapping.csx | | context.Body.data | Task yaniti | OutputHandler'lar | @@ -446,7 +441,6 @@ extended-tasks-test-workflow (F) | Task | `trigger-transition-task` | 12 | Tasks/task-execution/task-execution-test-workflow/trigger-transition-task.json | | Task | `subprocess-task` | 14 | Tasks/task-execution/task-execution-test-workflow/subprocess-task.json | | Task | `get-instances-task` | 15 | Tasks/task-execution/task-execution-test-workflow/get-instances-task.json | -| Task | `human-task` | 5 | Tasks/task-execution/task-execution-test-workflow/human-task.json | | Task | `dapr-http-task` | 1 | Tasks/task-execution/extended-tasks-test-workflow/dapr-http-task.json | | Task | `dapr-service-task` | 3 | Tasks/task-execution/extended-tasks-test-workflow/dapr-service-task.json | | Task | `dapr-binding-task` | 2 | Tasks/task-execution/extended-tasks-test-workflow/dapr-binding-task.json | @@ -1081,7 +1075,6 @@ Her grubun hangi vNext ozelliklerini test ettigini gosteren matris. **Gn** sutun | **Dapr Binding Task (2)** | | | X | | | | | | | | **Dapr Service Task (3)** | | | X | | | | | | | | **Dapr PubSub Task (4)** | | | X | | | | | | | -| **Human Task (5)** | | | X | | | | | | | | **Notification Task (10)** | | | X | | | | | | | | **Trigger Transition Task (12)** | | | X | | | | | | | | **SubProcess Task (14)** | | | X | | | | | | | diff --git a/tests/Core/Helpers/StateFunctionJson.cs b/tests/Core/Helpers/StateFunctionJson.cs index 8e56652..857977d 100644 --- a/tests/Core/Helpers/StateFunctionJson.cs +++ b/tests/Core/Helpers/StateFunctionJson.cs @@ -56,6 +56,85 @@ public static string ExtractStateName(JsonElement body) return null; } + /// + /// Returns activeCorrelations[] from a State function response body (root or under data). + /// Empty list when missing or not an array. See vnext-runtime/doc/tr/flow/function.md + /// "Sub-flow Korelasyonlari" tablosu (alanlar: correlationId, parentState, + /// subFlowInstanceId, subFlowType, subFlowDomain, subFlowName, + /// subFlowVersion, isCompleted, status). + /// + public static IReadOnlyList ExtractActiveCorrelations(JsonElement body) + { + if (body.ValueKind != JsonValueKind.Object) + return Array.Empty(); + + if ( + body.TryGetProperty("activeCorrelations", out var direct) + && direct.ValueKind == JsonValueKind.Array + ) + return direct.EnumerateArray().ToList(); + + if ( + body.TryGetProperty("data", out var data) + && data.ValueKind == JsonValueKind.Object + && data.TryGetProperty("activeCorrelations", out var nested) + && nested.ValueKind == JsonValueKind.Array + ) + return nested.EnumerateArray().ToList(); + + return Array.Empty(); + } + + /// + /// Locates the active correlation whose subFlowInstanceId equals . + /// Returns true and assigns on match. + /// Use after starting a SubProcess (tip 14) / SubFlow to verify runtime correlation tracking, + /// not just the parent attributes data — see vnext-tests-as-code skill section + /// "ActiveCorrelations ile SubProcess / SubFlow teyidi". + /// + public static bool TryFindActiveCorrelationBySubFlowInstanceId( + JsonElement body, + string subFlowInstanceId, + out JsonElement correlation + ) + { + correlation = default; + if (string.IsNullOrEmpty(subFlowInstanceId)) + return false; + + foreach (var c in ExtractActiveCorrelations(body)) + { + if ( + c.ValueKind == JsonValueKind.Object + && c.TryGetProperty("subFlowInstanceId", out var idEl) + && idEl.ValueKind == JsonValueKind.String + && string.Equals(idEl.GetString(), subFlowInstanceId, StringComparison.Ordinal) + ) + { + correlation = c; + return true; + } + } + + return false; + } + + /// + /// Reads subFlowType from a single correlation element (e.g. "P" for SubProcess, + /// "F"/"SubFlow" for SubFlow depending on runtime version). + /// + public static string? ExtractSubFlowType(JsonElement correlation) + { + if ( + correlation.ValueKind == JsonValueKind.Object + && correlation.TryGetProperty("subFlowType", out var t) + && t.ValueKind == JsonValueKind.String + ) + return t.GetString(); + + return null; + } + /// /// Whether .transitions[] contains an item whose name equals (primary for runtime state function), or whose key equals it if present. /// diff --git a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs index 138e1f9..f911383 100644 --- a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs +++ b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs @@ -14,15 +14,17 @@ internal static class TaskExecutionMainWorkflowInstanceDataAssertions "(task-execution-test-workflow mappings + task-execution.http B1 expected flags)"; /// - /// Human-task beklemeden önce: HTTP, script, timer-wait (Timer Task tip 9), start-flow, - /// get-instance-data, trigger, subprocess, get-instances ve human-task onEntry mapping - /// bayrakları. Notification task mapping.type: G kullandığı için - /// taskResults.notification entegrasyon assert'ine dahil edilmez. - /// timer-wait-state, scheduled transition (triggerType: 2) + ITimerMapping - /// ile 3 sn beklediği için timerStartedAt attribute'ü zorunludur ve test bunun - /// üzerinden gerçek bekleme süresini doğrular. + /// Happy path tamamlandiginda: HTTP, script, timer-wait (Timer Task tip 9), start-flow, + /// get-instance-data, trigger, subprocess ve get-instances onEntry mapping bayraklari. + /// Notification task mapping.type: G kullandigi icin taskResults.notification + /// entegrasyon assert'ine dahil edilmez. Human Task (tip 5) runtime tarafindan kaldirilacak + /// gecici bir ozellik oldugu icin bu workflow'da kullanilmaz; happy path human onayi olmadan + /// dogrudan completed-state'e ulasir. timer-wait-state, scheduled transition + /// (triggerType: 2) + ITimerMapping ile 3 sn bekledigi icin + /// timerStartedAt attribute'u zorunludur ve test bunun uzerinden gercek bekleme + /// suresini dogrular. /// - public static void AssertWhileWaitingOnHumanTask(JsonElement attributes) + public static void AssertHappyPathCompleted(JsonElement attributes) { JsonElementAssertions.AssertPropertyNonEmptyString( attributes, @@ -94,12 +96,6 @@ public static void AssertWhileWaitingOnHumanTask(JsonElement attributes) "completed", ContractHint ); - JsonElementAssertions.AssertNestedPropertyTrue( - attributes, - new[] { "taskResults", "humanTask" }, - "completed", - ContractHint - ); } } diff --git a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs index 5fbdf9c..062d9ee 100644 --- a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs +++ b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs @@ -33,7 +33,7 @@ public TaskExecutionTestWorkflowTests(VNextTestEnvironment environment) } [Fact] - public async Task B1_MainWorkflow_HappyPath_InstanceDataThenApprove_ReachesCompleted() + public async Task B1_MainWorkflow_HappyPath_ReachesCompleted() { var key = WorkflowInstanceTestHelper.UniqueInstanceKey("task-exec-it"); var instanceId = await _mainWorkflow.StartInstanceIdAsync( @@ -45,27 +45,33 @@ public async Task B1_MainWorkflow_HappyPath_InstanceDataThenApprove_ReachesCompl } ); + // Workflow zinciri: http -> script -> timer-wait (3 sn) -> start-flow -> get-instance-data + // -> notification -> trigger-transition -> subprocess -> get-instances -> completed-state. + // Human Task (tip 5) runtime tarafindan kaldirilacak gecici bir ozellik oldugu icin bu + // workflow'da kullanilmaz; happy path manuel onay olmadan dogrudan completed-state'e ulasir. await _mainWorkflow.WaitForStateAsync( instanceId, - "human-task-state", + "completed-state", MainWorkflowStateTimeout ); - var stateBody = await _mainWorkflow.GetStateFunctionBodyAsync(instanceId, headers: null); - Assert.True( - StateFunctionJson.TransitionsContainKey(stateBody, "approve-human-task"), - "human-task-state should expose approve-human-task transition" + var stateCompleted = await _mainWorkflow.GetStateFunctionBodyAsync( + instanceId, + headers: null ); + var status = StateFunctionJson.ExtractStatus(stateCompleted); + // Happy path bittiginde instance tamamlanmistir; GET .../functions/state govdesindeki status 'C' (Completed) olmalidir. + Assert.Equal("C", status); - var attrsBefore = await GetAttributesAsync(MainWorkflowKey, instanceId); - TaskExecutionMainWorkflowInstanceDataAssertions.AssertWhileWaitingOnHumanTask(attrsBefore); + var attrs = await GetAttributesAsync(MainWorkflowKey, instanceId); + TaskExecutionMainWorkflowInstanceDataAssertions.AssertHappyPathCompleted(attrs); // Timer Task (tip 9) gercekten beklemis mi? timer-wait-state'in scheduled transition'i // ITimerMapping ile 3 sn sonra start-flow-state'e gecirir; eger sadece auto transition - // calissaydi human-task-state'e ~yari saniyede ulasirdik. timerStartedAt parent attributes'unda - // yazildigi icin (TimerStartMapping) human-task-state'e ulasilan ana kadar gecen sure 3 sn'den + // calissaydi completed-state'e ~yari saniyede ulasirdik. timerStartedAt parent attributes'unda + // yazildigi icin (TimerStartMapping) completed-state'e ulasilan ana kadar gecen sure 3 sn'den // buyuk olmalidir. Toleransi 2.5 sn olarak aliyoruz; ust sinir koymuyoruz (CI yavasligi). - var timerStartedAtRaw = attrsBefore.GetProperty("timerStartedAt").GetString()!; + var timerStartedAtRaw = attrs.GetProperty("timerStartedAt").GetString()!; var timerStartedAt = DateTime.Parse( timerStartedAtRaw, System.Globalization.CultureInfo.InvariantCulture, @@ -79,10 +85,10 @@ await _mainWorkflow.WaitForStateAsync( + "Eger bu deger 3 sn'den kucukse scheduled transition timer'i devre disi kalmis olabilir." ); - // StartFlow + DirectTrigger zincirinin sadece parent instance'a `startedInstanceId` yazmış olması yeterli değildir. - // Hedef workflow'un `functions/state` çağrısıyla; (1) StartTask yeni bir instance açtı, (2) DirectTriggerTask - // hedefin manuel `manual-complete-target` geçişini gerçekten tetikledi, doğrulanır. - var startedTargetId = attrsBefore.GetProperty("startedInstanceId").GetString(); + // StartFlow + DirectTrigger zincirinin sadece parent instance'a `startedInstanceId` yazmis olmasi yeterli degildir. + // Hedef workflow'un `functions/state` cagrisiyla; (1) StartTask yeni bir instance acti, (2) DirectTriggerTask + // hedefin manuel `manual-complete-target` gecisini gercekten tetikledi, dogrulanir. + var startedTargetId = attrs.GetProperty("startedInstanceId").GetString(); Assert.False( string.IsNullOrWhiteSpace(startedTargetId), "startedInstanceId should be a non-empty string from StartFlowMapping output. Because startflow task should start a workflow." @@ -103,7 +109,7 @@ await _mainWorkflow.WaitForStateAsync( // varligi ve hedef workflow GET functions/state cevabinin Active/Completed olmasi, subprocess'in // gercekten baslatildigini kanitlar (sadece "completed" bayragi yetmez). Ayrica GetInstance ile // hedef instance attributes'inda parentInstanceId/source/note alanlarinin yazildigini dogrulariz. - var subprocessInstanceId = attrsBefore.GetProperty("subprocessInstanceId").GetString(); + var subprocessInstanceId = attrs.GetProperty("subprocessInstanceId").GetString(); Assert.False( string.IsNullOrWhiteSpace(subprocessInstanceId), "subprocessInstanceId should be a non-empty string from SubProcessMapping output" @@ -146,25 +152,36 @@ await _mainWorkflow.WaitForStateAsync( "subprocess instance attributes.note (set via SubProcessTask body)" ); - await _mainWorkflow.RunTransitionAsync( - instanceId, - "approve-human-task", - headers: null, - transitionBody: new { } + // SubProcess (tip 14) baslatildiginda parent instance'in `functions/state` cevabindaki + // `activeCorrelations` listesinde subprocess'e karsilik gelen bir correlation bulunur: + // - `subFlowInstanceId` parent attributes'taki `subprocessInstanceId` ile birebir esit, + // - `subFlowType` SubProcess kisa kodu olan "P". + // Bu, parent'in attributes'ina yazilan id'nin (mapping uretimi) yaninda runtime'in da + // sub-flow correlation'ini gercekten kaydettigini kanitlar; sadece "completed = true" + // bayragi yetmez (vnext-runtime/doc/tr/flow/function.md "Sub-flow Korelasyonlari" tablosu; + // vnext-tests-as-code skill "ActiveCorrelations ile SubProcess / SubFlow teyidi" bolumu). + // Not: Eger parent COMPLETED iken activeCorrelations bos donerse bu adim fail olur ve + // doğrulamayi parent subprocess'i baslattiktan hemen sonraki bir state snapshot'inda + // yapmaya tasimak gerekir; ilk fail durumunda hata mesaji bu durumu da belirtir. + var correlationFound = StateFunctionJson.TryFindActiveCorrelationBySubFlowInstanceId( + stateCompleted, + subprocessInstanceId!, + out var subprocessCorrelation ); - - await _mainWorkflow.AssertStateAsync(instanceId, "completed-state"); - - var stateCompleted = await _mainWorkflow.GetStateFunctionBodyAsync( - instanceId, - headers: null + var allCorrelations = StateFunctionJson.ExtractActiveCorrelations(stateCompleted); + Assert.True( + correlationFound, + $"parent functions/state.activeCorrelations icinde subFlowInstanceId == '{subprocessInstanceId}' olan correlation bulunmali; " + + $"toplam correlation sayisi = {allCorrelations.Count}. " + + "Eger 0 geldiyse parent COMPLETED'a ulastiginda runtime correlation'i listeden cikarmis olabilir; " + + "doğrulamayi parent subprocess-state'inde iken alinan bir snapshot'a tasimak gerekebilir." ); - var status = StateFunctionJson.ExtractStatus(stateCompleted); - // Happy path bittiğinde instance tamamlanmıştır; GET .../functions/state gövdesindeki status 'C' (Completed) olmalıdır. - Assert.Equal("C", status); - var attrsAfter = await GetAttributesAsync(MainWorkflowKey, instanceId); - TaskExecutionMainWorkflowInstanceDataAssertions.AssertWhileWaitingOnHumanTask(attrsAfter); + var subFlowType = StateFunctionJson.ExtractSubFlowType(subprocessCorrelation); + Assert.True( + string.Equals(subFlowType, "P", StringComparison.Ordinal), + $"activeCorrelations[].subFlowType beklenen 'P' (SubProcess kisa kodu); actual = '{subFlowType ?? ""}'." + ); } [Fact] From 21c0389ceb9158df140ba3b6aa207821fb76ae0f Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Mon, 4 May 2026 12:29:57 +0300 Subject: [PATCH 11/24] extended tasks test has done --- core.link.json | 2 +- .../dapr-binding-task.json | 15 -- .../dapr-pubsub-task.json | 6 +- .../error-boundary-test-workflow.json | 3 +- .../.meta/core-flow-test.diagram.json | 3 + .../.meta/subprocess-flow-test.diagram.json | 3 + ...ance-management-test-workflow.diagram.json | 3 + .../task-execution-test-workflow.diagram.json | 4 +- .../extended-tasks-test-workflow.json | 62 +------- .../DaprBindingMapping.csx | 40 ----- .../DaprPubSubMapping.csx | 141 ++++++++++++++++-- doc/integration-test-documentation.md | 52 +++++-- tests/Core/Helpers/JsonElementAssertions.cs | 26 ++++ .../TaskExecutionInstanceDataAssertions.cs | 35 ++++- .../TaskExecutionTestWorkflowTests.cs | 10 ++ 15 files changed, 262 insertions(+), 143 deletions(-) delete mode 100644 core/Tasks/task-execution/extended-tasks-test-workflow/dapr-binding-task.json create mode 100644 core/Workflows/flow-types/.meta/core-flow-test.diagram.json create mode 100644 core/Workflows/flow-types/.meta/subprocess-flow-test.diagram.json create mode 100644 core/Workflows/instance-management/.meta/instance-management-test-workflow.diagram.json delete mode 100644 core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprBindingMapping.csx diff --git a/core.link.json b/core.link.json index 8b9906a..b99b734 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-04-30T14:29:58.117Z" + "importedAt": "2026-05-04T09:07:01.988Z" } \ No newline at end of file diff --git a/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-binding-task.json b/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-binding-task.json deleted file mode 100644 index ccda07f..0000000 --- a/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-binding-task.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "key": "dapr-binding-task", - "version": "1.0.0", - "domain": "core", - "flow": "sys-tasks", - "flowVersion": "1.0.0", - "tags": ["integration-test", "task-execution", "dapr-binding"], - "attributes": { - "type": "2", - "config": { - "bindingName": "test-binding", - "operation": "create" - } - } -} diff --git a/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-pubsub-task.json b/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-pubsub-task.json index 8d7acda..c660fd6 100644 --- a/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-pubsub-task.json +++ b/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-pubsub-task.json @@ -9,7 +9,11 @@ "type": "4", "config": { "pubSubName": "test-pubsub", - "topic": "test-topic" + "topic": "test-topic", + "data": { + "eventType": "IntegrationTest", + "source": "extended-tasks-test-workflow" + } } } } diff --git a/core/Workflows/error-boundary/error-boundary-test-workflow.json b/core/Workflows/error-boundary/error-boundary-test-workflow.json index 06f0609..488872d 100644 --- a/core/Workflows/error-boundary/error-boundary-test-workflow.json +++ b/core/Workflows/error-boundary/error-boundary-test-workflow.json @@ -259,7 +259,8 @@ "onError": [ { "action": 2, - "priority": 10 + "priority": 10, + "transition": "auto-to-log-from-rollback" } ] }, diff --git a/core/Workflows/flow-types/.meta/core-flow-test.diagram.json b/core/Workflows/flow-types/.meta/core-flow-test.diagram.json new file mode 100644 index 0000000..00a4467 --- /dev/null +++ b/core/Workflows/flow-types/.meta/core-flow-test.diagram.json @@ -0,0 +1,3 @@ +{ + "nodePos": {} +} \ No newline at end of file diff --git a/core/Workflows/flow-types/.meta/subprocess-flow-test.diagram.json b/core/Workflows/flow-types/.meta/subprocess-flow-test.diagram.json new file mode 100644 index 0000000..00a4467 --- /dev/null +++ b/core/Workflows/flow-types/.meta/subprocess-flow-test.diagram.json @@ -0,0 +1,3 @@ +{ + "nodePos": {} +} \ No newline at end of file diff --git a/core/Workflows/instance-management/.meta/instance-management-test-workflow.diagram.json b/core/Workflows/instance-management/.meta/instance-management-test-workflow.diagram.json new file mode 100644 index 0000000..00a4467 --- /dev/null +++ b/core/Workflows/instance-management/.meta/instance-management-test-workflow.diagram.json @@ -0,0 +1,3 @@ +{ + "nodePos": {} +} \ No newline at end of file diff --git a/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json b/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json index e3de053..befb91e 100644 --- a/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json +++ b/core/Workflows/task-execution/.meta/task-execution-test-workflow.diagram.json @@ -1,8 +1,8 @@ { "nodePos": { "http-task-state": { - "x": 690, - "y": 320 + "x": 660, + "y": 330 }, "script-processing-state": { "x": 950, diff --git a/core/Workflows/task-execution/extended-tasks-test-workflow.json b/core/Workflows/task-execution/extended-tasks-test-workflow.json index 1bf4ae5..493a98f 100644 --- a/core/Workflows/task-execution/extended-tasks-test-workflow.json +++ b/core/Workflows/task-execution/extended-tasks-test-workflow.json @@ -1,6 +1,6 @@ { "key": "extended-tasks-test-workflow", - "version": "1.0.0", + "version": "1.2.0", "domain": "core", "flow": "sys-flows", "flowVersion": "1.0.0", @@ -8,7 +8,6 @@ "integration-test", "task-execution", "dapr-http", - "dapr-binding", "dapr-service", "dapr-pubsub" ], @@ -194,63 +193,6 @@ } ], "onExits": [], - "transitions": [ - { - "key": "auto-to-dapr-binding", - "target": "dapr-binding-state", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Auto to Dapr Binding" - }, - { - "language": "tr-TR", - "label": "Dapr Binding'e Otomatik" - } - ], - "rule": { - "location": "./src/shared/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" - }, - "onExecutionTasks": [] - } - ] - }, - { - "key": "dapr-binding-state", - "stateType": 2, - "subType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Dapr Binding Task" - }, - { - "language": "tr-TR", - "label": "Dapr Binding Gorevi" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - { - "order": 1, - "task": { - "key": "dapr-binding-task", - "version": "1.0.0", - "domain": "core", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/extended-tasks-test-workflow/DaprBindingMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIERhcHJCaW5kaW5nTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpbml0Q29tcGxldGVkIikpIHJlc3VsdC5pbml0Q29tcGxldGVkID0gZGF0YS5pbml0Q29tcGxldGVkOwoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGFza1Jlc3VsdHMiKSkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwckh0dHAiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJIdHRwID0gZGF0YS50YXNrUmVzdWx0cy5kYXBySHR0cDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXByU2VydmljZSIpKSByZXN1bHQudGFza1Jlc3VsdHMuZGFwclNlcnZpY2UgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJTZXJ2aWNlOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgIm5vdGlmaWNhdGlvbiIpKSByZXN1bHQudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uID0gZGF0YS50YXNrUmVzdWx0cy5ub3RpZmljYXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAidHJpZ2dlclRyYW5zaXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uID0gZGF0YS50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJnZXRJbnN0YW5jZXMiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IGRhdGEudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkgcmVzdWx0LnRhc2tSZXN1bHRzLnN1YnByb2Nlc3MgPSBkYXRhLnRhc2tSZXN1bHRzLnN1YnByb2Nlc3M7CiAgICAgICAgfQoKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwckJpbmRpbmcgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByQmluZGluZy5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByQmluZGluZy5leGVjdXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCJEYXByQmluZGluZ01hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" - } - } - ], - "onExits": [], "transitions": [ { "key": "auto-to-dapr-pubsub", @@ -303,7 +245,7 @@ }, "mapping": { "location": "./src/extended-tasks-test-workflow/DaprPubSubMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIERhcHJQdWJTdWJNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImluaXRDb21wbGV0ZWQiKSkgcmVzdWx0LmluaXRDb21wbGV0ZWQgPSBkYXRhLmluaXRDb21wbGV0ZWQ7CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0YXNrUmVzdWx0cyIpKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXBySHR0cCIpKSByZXN1bHQudGFza1Jlc3VsdHMuZGFwckh0dHAgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJIdHRwOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImRhcHJTZXJ2aWNlIikpIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByU2VydmljZSA9IGRhdGEudGFza1Jlc3VsdHMuZGFwclNlcnZpY2U7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZGFwckJpbmRpbmciKSkgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJCaW5kaW5nID0gZGF0YS50YXNrUmVzdWx0cy5kYXByQmluZGluZzsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInRyaWdnZXJUcmFuc2l0aW9uIikpIHJlc3VsdC50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb247CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLnRhc2tSZXN1bHRzLCAiZ2V0SW5zdGFuY2VzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJzdWJwcm9jZXNzIikpIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzID0gZGF0YS50YXNrUmVzdWx0cy5zdWJwcm9jZXNzOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByUHViU3ViLmNvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIuZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiRGFwclB1YlN1Yk1hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRleHQuSnNvbjsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBEYXByUHViU3ViTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIC8vLyA8c3VtbWFyeT4KICAgIC8vLyBUYXNrRXhlY3V0aW9uRW5naW5lLCB0YXNrIHNvbnVjdW51IEpzb25TZXJpYWxpemVyIGlsZSB5YXphcmtlbiBFeHBhbmRvT2JqZWN0IGnDp2luZGVraQogICAgLy8vIEpzb25FbGVtZW50IHJlZmVyYW5zbGFyxLFuxLEgc2VyaWxlxZ90aXJlbWl5b3IgKCJPcGVyYXRpb24gaXMgbm90IHZhbGlkIGR1ZSB0byB0aGUgY3VycmVudCBzdGF0ZSBvZiB0aGUgb2JqZWN0IikuCiAgICAvLy8gY29udGV4dC5Cb2R5LmlzU3VjY2VzcyAvIHN0YXR1c0NvZGUgcnVudGltZSdkYSBKc29uRWxlbWVudCBvbGFyYWsgZ2VsZWJpbGlyIOKAlCBDTFIgdGlwaW5lIMOnZXZpcml5b3J1ei4KICAgIC8vLyA8L3N1bW1hcnk+CiAgICBwcml2YXRlIHN0YXRpYyBib29sIENvZXJjZUJvb2wob2JqZWN0IG8pCiAgICB7CiAgICAgICAgaWYgKG8gPT0gbnVsbCkKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIGlmIChvIGlzIGJvb2wgYikKICAgICAgICAgICAgcmV0dXJuIGI7CiAgICAgICAgaWYgKG8gaXMgSnNvbkVsZW1lbnQgamUpCiAgICAgICAgewogICAgICAgICAgICByZXR1cm4gamUuVmFsdWVLaW5kIHN3aXRjaAogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBKc29uVmFsdWVLaW5kLlRydWUgPT4gdHJ1ZSwKICAgICAgICAgICAgICAgIEpzb25WYWx1ZUtpbmQuRmFsc2UgPT4gZmFsc2UsCiAgICAgICAgICAgICAgICBKc29uVmFsdWVLaW5kLlN0cmluZyA9PiBib29sLlRyeVBhcnNlKGplLkdldFN0cmluZygpLCBvdXQgdmFyIHApICYmIHAsCiAgICAgICAgICAgICAgICBKc29uVmFsdWVLaW5kLk51bWJlciA9PiBqZS5UcnlHZXRJbnQzMihvdXQgdmFyIG4pID8gbiAhPSAwIDogZmFsc2UsCiAgICAgICAgICAgICAgICBfID0+IGZhbHNlLAogICAgICAgICAgICB9OwogICAgICAgIH0KICAgICAgICBpZiAobyBpcyBzdHJpbmcgcykKICAgICAgICAgICAgcmV0dXJuIGJvb2wuVHJ5UGFyc2Uocywgb3V0IHZhciB4KSAmJiB4OwogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgcmV0dXJuIENvbnZlcnQuVG9Cb29sZWFuKG8pOwogICAgICAgIH0KICAgICAgICBjYXRjaAogICAgICAgIHsKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KICAgIH0KCiAgICBwcml2YXRlIHN0YXRpYyBpbnQ/IENvZXJjZUludDMyKG9iamVjdCBvKQogICAgewogICAgICAgIGlmIChvID09IG51bGwpCiAgICAgICAgICAgIHJldHVybiBudWxsOwogICAgICAgIGlmIChvIGlzIGludCBpKQogICAgICAgICAgICByZXR1cm4gaTsKICAgICAgICBpZiAobyBpcyBsb25nIGwpCiAgICAgICAgICAgIHJldHVybiBjaGVja2VkKChpbnQpbCk7CiAgICAgICAgaWYgKG8gaXMgSnNvbkVsZW1lbnQgamUgJiYgamUuVmFsdWVLaW5kID09IEpzb25WYWx1ZUtpbmQuTnVtYmVyICYmIGplLlRyeUdldEludDMyKG91dCB2YXIgbikpCiAgICAgICAgICAgIHJldHVybiBuOwogICAgICAgIGlmIChvIGlzIHN0cmluZyBzICYmIGludC5UcnlQYXJzZShzLCBvdXQgdmFyIHApKQogICAgICAgICAgICByZXR1cm4gcDsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIHJldHVybiBDb252ZXJ0LlRvSW50MzIobyk7CiAgICAgICAgfQogICAgICAgIGNhdGNoCiAgICAgICAgewogICAgICAgICAgICByZXR1cm4gbnVsbDsKICAgICAgICB9CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBwdWJzdWJUYXNrID0gKHRhc2sgYXMgRGFwclB1YlN1YlRhc2spITsKICAgICAgICBpZiAocHVic3ViVGFzayA9PSBudWxsKQogICAgICAgICAgICB0aHJvdyBuZXcgSW52YWxpZE9wZXJhdGlvbkV4Y2VwdGlvbigiVGFzayBtdXN0IGJlIGEgRGFwclB1YlN1YlRhc2siKTsKCiAgICAgICAgLy8gVmF1bHQndGEgRGFwclB1YlN1Yk5hbWUgdmFyc2Egb251IGt1bGxhbiwgeW9rc2EgdGFzayBKU09OJ2Rha2kgZGVmYXVsdCAodGVzdC1wdWJzdWIpIGdlY2VybGkga2FsaXIuCiAgICAgICAgLy8gQm95bGVjZSBkZXZlbG9wbWVudCd0ZSBWYXVsdCBjb25maWcnaSBvcHNpeW9uZWw7IGZhcmtsaSBvcnRhbWRhIChvcm5lZ2luIHZuZXh0LXB1YnN1YikKICAgICAgICAvLyBzYWRlY2UgVmF1bHQnYSBrZXkgZWtsZW1layB5ZXRlcmxpLgogICAgICAgIHN0cmluZyB2YXVsdFZhbHVlID0gR2V0Q29uZmlnVmFsdWUoIkRhcHJQdWJTdWJOYW1lIik7CiAgICAgICAgaWYgKCFzdHJpbmcuSXNOdWxsT3JXaGl0ZVNwYWNlKHZhdWx0VmFsdWUpKQogICAgICAgIHsKICAgICAgICAgICAgcHVic3ViVGFzay5TZXRQdWJTdWJOYW1lKHZhdWx0VmFsdWUpOwogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkRhcHJQdWJTdWJNYXBwaW5nOiBWYXVsdCBvdmVycmlkZSBwdWJTdWJOYW1lID0ge3ZhdWx0VmFsdWV9Iik7CiAgICAgICAgfQogICAgICAgIGVsc2UKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKAogICAgICAgICAgICAgICAgJCJEYXByUHViU3ViTWFwcGluZzogVmF1bHQndGEgRGFwclB1YlN1Yk5hbWUgeW9rLCB0YXNrIEpTT04gZGVmYXVsdCBrdWxsYW5pbGl5b3IiCiAgICAgICAgICAgICk7CiAgICAgICAgfQoKICAgICAgICAvLyBQdWJsaXNoIGljaW4gYGRhdGFgIG51bGwgb2xhbWF6IChBcmd1bWVudE51bGxFeGNlcHRpb24pLiBUYXNrIEpTT04nZGEgZGF0YSBvbHNhIGJpbGUKICAgICAgICAvLyBlc2tpIHB1Ymxpc2ggLyBzaXJhc2kgecO8esO8bmRlbiBnw7x2ZW4gdmVybWV6OyBtYXBwaW5nIGlsZSBnYXJhbnRpIGV0LgogICAgICAgIHZhciBpbnN0YW5jZURhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyBtZXNzYWdlRGF0YSA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgbWVzc2FnZURhdGEuZXZlbnRUeXBlID0gIkludGVncmF0aW9uVGVzdCI7CiAgICAgICAgbWVzc2FnZURhdGEuc291cmNlID0gImV4dGVuZGVkLXRhc2tzLXRlc3Qtd29ya2Zsb3ciOwogICAgICAgIG1lc3NhZ2VEYXRhLnRpbWVzdGFtcCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShpbnN0YW5jZURhdGEsICJ0ZXN0SWQiKSkKICAgICAgICAgICAgbWVzc2FnZURhdGEudGVzdElkID0gaW5zdGFuY2VEYXRhLnRlc3RJZDsKICAgICAgICBwdWJzdWJUYXNrLlNldERhdGEobWVzc2FnZURhdGEpOwogICAgICAgIExvZ0luZm9ybWF0aW9uKCJEYXByUHViU3ViTWFwcGluZzogU2V0RGF0YSB0YW1hbWxhbmRpIik7CgogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpCiAgICAgICAgICAgIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImluaXRDb21wbGV0ZWQiKSkKICAgICAgICAgICAgcmVzdWx0LmluaXRDb21wbGV0ZWQgPSBkYXRhLmluaXRDb21wbGV0ZWQ7CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0YXNrUmVzdWx0cyIpKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXBySHR0cCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJIdHRwID0gZGF0YS50YXNrUmVzdWx0cy5kYXBySHR0cDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXByU2VydmljZSIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJTZXJ2aWNlID0gZGF0YS50YXNrUmVzdWx0cy5kYXByU2VydmljZTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXByQmluZGluZyIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJCaW5kaW5nID0gZGF0YS50YXNrUmVzdWx0cy5kYXByQmluZGluZzsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkKICAgICAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uID0gZGF0YS50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJnZXRJbnN0YW5jZXMiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJzdWJwcm9jZXNzIikpCiAgICAgICAgICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2VzcyA9IGRhdGEudGFza1Jlc3VsdHMuc3VicHJvY2VzczsKICAgICAgICB9CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByUHViU3ViID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwclB1YlN1Yi5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByUHViU3ViLmV4ZWN1dGVkQXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKCiAgICAgICAgLy8gU2tpbGwgdm5leHQtd29ya2Zsb3ctY3JlYXRpb24gwqc2LjQ6ICJjb21wbGV0ZWQgPSB0cnVlIiBsaXRlcmFsIG1hcHBpbmcgw6dhxJ9yxLFsZMSxxJ/EsW7EsQogICAgICAgIC8vIGfDtnN0ZXJpciwgdGFzaydpbiBiYcWfYXLEsWzEsSBvbGR1xJ91bnUga2FuxLF0bGFtYXouIERhcHIgUHViU3ViIHRhc2snaW5kZSBmaXJlLWFuZC1mb3JnZXQKICAgICAgICAvLyBvbGR1xJ91bmRhbiBkw7ZuZW4gdmVyaSB5b2s7IGFtYSBydW50aW1lIGNvbnRleHQuQm9keSB6YXJmxLFuZGEgdGFzayBzb251Y3UgacOnaW4KICAgICAgICAvLyBpc1N1Y2Nlc3MgKGJvb2wpIHZlIHN0YXR1c0NvZGUgKGludCkgYWxhbmxhcsSxbsSxIGTDtm5lci4gQnVubGFyxLEgcGFyZW50IGF0dHJpYnV0ZXMnYQogICAgICAgIC8vIHlhesSxcCB0ZXN0dGUgYXNzZXJ0IGVkZXJlayAidGFzayBnZXLDp2VrdGVuIHB1Ymxpc2ggZWRpbGRpIiBpZGRpYXPEsW7EsSBrYW7EsXRsxLF5b3J1ei4KICAgICAgICB2YXIgdGFza1Jlc3BvbnNlID0gY29udGV4dC5Cb2R5OwogICAgICAgIGlmICh0YXNrUmVzcG9uc2UgIT0gbnVsbCkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eSh0YXNrUmVzcG9uc2UsICJpc1N1Y2Nlc3MiKSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgZHluYW1pYyB0ciA9IHRhc2tSZXNwb25zZTsKICAgICAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByUHViU3ViLnB1Ymxpc2hlZCA9IENvZXJjZUJvb2wodHIuaXNTdWNjZXNzKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkodGFza1Jlc3BvbnNlLCAic3RhdHVzQ29kZSIpKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBkeW5hbWljIHRyID0gdGFza1Jlc3BvbnNlOwogICAgICAgICAgICAgICAgdmFyIGNvZGUgPSBDb2VyY2VJbnQzMih0ci5zdGF0dXNDb2RlKTsKICAgICAgICAgICAgICAgIGlmIChjb2RlLkhhc1ZhbHVlKQogICAgICAgICAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByUHViU3ViLnN0YXR1c0NvZGUgPSBjb2RlLlZhbHVlOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiRGFwclB1YlN1Yk1hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } ], diff --git a/core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprBindingMapping.csx b/core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprBindingMapping.csx deleted file mode 100644 index f8004bb..0000000 --- a/core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprBindingMapping.csx +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Dynamic; -using System.Threading.Tasks; -using BBT.Workflow.Scripting; -using BBT.Workflow.Definitions; - -public class DaprBindingMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public Task OutputHandler(ScriptContext context) - { - var data = context.Instance.Data; - dynamic result = new ExpandoObject(); - - if (HasProperty(data, "testId")) result.testId = data.testId; - if (HasProperty(data, "initCompleted")) result.initCompleted = data.initCompleted; - - result.taskResults = new ExpandoObject(); - if (HasProperty(data, "taskResults")) - { - if (HasProperty(data.taskResults, "daprHttp")) result.taskResults.daprHttp = data.taskResults.daprHttp; - if (HasProperty(data.taskResults, "daprService")) result.taskResults.daprService = data.taskResults.daprService; - if (HasProperty(data.taskResults, "notification")) result.taskResults.notification = data.taskResults.notification; - if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; - if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; - if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; - } - - result.taskResults.daprBinding = new ExpandoObject(); - result.taskResults.daprBinding.completed = true; - result.taskResults.daprBinding.executedAt = DateTime.UtcNow.ToString("o"); - - LogInformation("DaprBindingMapping completed"); - return Task.FromResult(new ScriptResponse { Data = result }); - } -} diff --git a/core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprPubSubMapping.csx b/core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprPubSubMapping.csx index f88eaa3..c8ad5ee 100644 --- a/core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprPubSubMapping.csx +++ b/core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprPubSubMapping.csx @@ -1,13 +1,103 @@ using System; using System.Dynamic; +using System.Text.Json; using System.Threading.Tasks; -using BBT.Workflow.Scripting; using BBT.Workflow.Definitions; +using BBT.Workflow.Scripting; +using BBT.Workflow.Scripting.Functions; public class DaprPubSubMapping : ScriptBase, IMapping { + /// + /// TaskExecutionEngine, task sonucunu JsonSerializer ile yazarken ExpandoObject içindeki + /// JsonElement referanslarını serileştiremiyor ("Operation is not valid due to the current state of the object"). + /// context.Body.isSuccess / statusCode runtime'da JsonElement olarak gelebilir — CLR tipine çeviriyoruz. + /// + private static bool CoerceBool(object o) + { + if (o == null) + return false; + if (o is bool b) + return b; + if (o is JsonElement je) + { + return je.ValueKind switch + { + JsonValueKind.True => true, + JsonValueKind.False => false, + JsonValueKind.String => bool.TryParse(je.GetString(), out var p) && p, + JsonValueKind.Number => je.TryGetInt32(out var n) ? n != 0 : false, + _ => false, + }; + } + if (o is string s) + return bool.TryParse(s, out var x) && x; + try + { + return Convert.ToBoolean(o); + } + catch + { + return false; + } + } + + private static int? CoerceInt32(object o) + { + if (o == null) + return null; + if (o is int i) + return i; + if (o is long l) + return checked((int)l); + if (o is JsonElement je && je.ValueKind == JsonValueKind.Number && je.TryGetInt32(out var n)) + return n; + if (o is string s && int.TryParse(s, out var p)) + return p; + try + { + return Convert.ToInt32(o); + } + catch + { + return null; + } + } + public Task InputHandler(WorkflowTask task, ScriptContext context) { + var pubsubTask = (task as DaprPubSubTask)!; + if (pubsubTask == null) + throw new InvalidOperationException("Task must be a DaprPubSubTask"); + + // Vault'ta DaprPubSubName varsa onu kullan, yoksa task JSON'daki default (test-pubsub) gecerli kalir. + // Boylece development'te Vault config'i opsiyonel; farkli ortamda (ornegin vnext-pubsub) + // sadece Vault'a key eklemek yeterli. + string vaultValue = GetConfigValue("DaprPubSubName"); + if (!string.IsNullOrWhiteSpace(vaultValue)) + { + pubsubTask.SetPubSubName(vaultValue); + LogInformation($"DaprPubSubMapping: Vault override pubSubName = {vaultValue}"); + } + else + { + LogInformation( + $"DaprPubSubMapping: Vault'ta DaprPubSubName yok, task JSON default kullaniliyor" + ); + } + + // Publish icin `data` null olamaz (ArgumentNullException). Task JSON'da data olsa bile + // eski publish / sirasi yüzünden güven vermez; mapping ile garanti et. + var instanceData = context.Instance.Data; + dynamic messageData = new ExpandoObject(); + messageData.eventType = "IntegrationTest"; + messageData.source = "extended-tasks-test-workflow"; + messageData.timestamp = DateTime.UtcNow.ToString("o"); + if (HasProperty(instanceData, "testId")) + messageData.testId = instanceData.testId; + pubsubTask.SetData(messageData); + LogInformation("DaprPubSubMapping: SetData tamamlandi"); + return Task.FromResult(new ScriptResponse()); } @@ -16,25 +106,56 @@ public class DaprPubSubMapping : ScriptBase, IMapping var data = context.Instance.Data; dynamic result = new ExpandoObject(); - if (HasProperty(data, "testId")) result.testId = data.testId; - if (HasProperty(data, "initCompleted")) result.initCompleted = data.initCompleted; + if (HasProperty(data, "testId")) + result.testId = data.testId; + if (HasProperty(data, "initCompleted")) + result.initCompleted = data.initCompleted; result.taskResults = new ExpandoObject(); if (HasProperty(data, "taskResults")) { - if (HasProperty(data.taskResults, "daprHttp")) result.taskResults.daprHttp = data.taskResults.daprHttp; - if (HasProperty(data.taskResults, "daprService")) result.taskResults.daprService = data.taskResults.daprService; - if (HasProperty(data.taskResults, "daprBinding")) result.taskResults.daprBinding = data.taskResults.daprBinding; - if (HasProperty(data.taskResults, "notification")) result.taskResults.notification = data.taskResults.notification; - if (HasProperty(data.taskResults, "triggerTransition")) result.taskResults.triggerTransition = data.taskResults.triggerTransition; - if (HasProperty(data.taskResults, "getInstances")) result.taskResults.getInstances = data.taskResults.getInstances; - if (HasProperty(data.taskResults, "subprocess")) result.taskResults.subprocess = data.taskResults.subprocess; + if (HasProperty(data.taskResults, "daprHttp")) + result.taskResults.daprHttp = data.taskResults.daprHttp; + if (HasProperty(data.taskResults, "daprService")) + result.taskResults.daprService = data.taskResults.daprService; + if (HasProperty(data.taskResults, "daprBinding")) + result.taskResults.daprBinding = data.taskResults.daprBinding; + if (HasProperty(data.taskResults, "notification")) + result.taskResults.notification = data.taskResults.notification; + if (HasProperty(data.taskResults, "triggerTransition")) + result.taskResults.triggerTransition = data.taskResults.triggerTransition; + if (HasProperty(data.taskResults, "getInstances")) + result.taskResults.getInstances = data.taskResults.getInstances; + if (HasProperty(data.taskResults, "subprocess")) + result.taskResults.subprocess = data.taskResults.subprocess; } result.taskResults.daprPubSub = new ExpandoObject(); result.taskResults.daprPubSub.completed = true; result.taskResults.daprPubSub.executedAt = DateTime.UtcNow.ToString("o"); + // Skill vnext-workflow-creation §6.4: "completed = true" literal mapping çağrıldığını + // gösterir, task'in başarılı olduğunu kanıtlamaz. Dapr PubSub task'inde fire-and-forget + // olduğundan dönen veri yok; ama runtime context.Body zarfında task sonucu için + // isSuccess (bool) ve statusCode (int) alanlarını döner. Bunları parent attributes'a + // yazıp testte assert ederek "task gerçekten publish edildi" iddiasını kanıtlıyoruz. + var taskResponse = context.Body; + if (taskResponse != null) + { + if (HasProperty(taskResponse, "isSuccess")) + { + dynamic tr = taskResponse; + result.taskResults.daprPubSub.published = CoerceBool(tr.isSuccess); + } + if (HasProperty(taskResponse, "statusCode")) + { + dynamic tr = taskResponse; + var code = CoerceInt32(tr.statusCode); + if (code.HasValue) + result.taskResults.daprPubSub.statusCode = code.Value; + } + } + LogInformation("DaprPubSubMapping completed"); return Task.FromResult(new ScriptResponse { Data = result }); } diff --git a/doc/integration-test-documentation.md b/doc/integration-test-documentation.md index f9d44ce..582af30 100644 --- a/doc/integration-test-documentation.md +++ b/doc/integration-test-documentation.md @@ -10,7 +10,7 @@ Bu dokuman, `core` projesindeki tum integration test workflow'larini, icerdikler |---|------|----------|---------------------------| | 1 | Lifecycle & Transitions | `lifecycle-transitions-test-workflow` | State tipleri, transition tipleri, on-entries/exits, timer, cancel, **exit** (`attributes.exit`), **schedule iptal / timer reschedule**, **queryRoles** ve **transition `roles` (yalnizca state fonksiyonu listeleme filtresi)** | | 2 | SubFlow & SubProcess | `subflow-orchestration-parent` + child + grandchild | Parent-child-grandchild zinciri, parent shared transitions, **child shared transition**, **cancel cascade** (child/grandchild **cancelled** final state'leri), **effective state**, **updateData (SubFlow)** | -| 3 | Task Execution | `task-execution-test-workflow` + `task-target-workflow` + `extended-tasks-test-workflow` | HTTP/Script/StartFlow/GetInstanceData/Notification/TriggerTransition/SubProcess/GetInstances/Human; Dapr HTTP/Service/Binding/PubSub (`extended-tasks-test-workflow`); Mocklab + Dapr YAML | +| 3 | Task Execution | `task-execution-test-workflow` + `task-target-workflow` + `extended-tasks-test-workflow` | HTTP/Script/StartFlow/GetInstanceData/Notification/TriggerTransition/SubProcess/GetInstances; Dapr HTTP/Service/PubSub (`extended-tasks-test-workflow`); Mocklab + Dapr YAML | | 4 | Error Boundary | `error-boundary-test-workflow` | Retry, Ignore, **Rollback**, **Log**, **Notify** (action:4) aksiyonlari, retry policy, priority, **timeoutPolicy** (onTimeout), **errorHandlerRule** (errorTypes/errorCodes) | | 5 | View, Function & Extension | `view-function-extension-test-workflow` | View tipleri, display modlari, function, extension, wizard state, **features** referansi | | 6 | Schema & Data | `schema-data-test-workflow` | Master schema, transition schema, updateData, field roles | @@ -27,7 +27,7 @@ Bu dokuman, `core` projesindeki tum integration test workflow'larini, icerdikler ``` core/ ├── Workflows/ (15 workflow - 13 mevcut + 2 yeni version-consistency) -├── Tasks/ (23 task - 22 mevcut + 1 yeni version-consistency) +├── Tasks/ (22 task - 21 mevcut + 1 yeni version-consistency; Dapr Binding task kaldirildi) ├── Views/ (3 view) ├── Schemas/ (2 schema) ├── Functions/ (2 function) @@ -325,7 +325,7 @@ subflow-orchestration-parent (F) **Workflow'lar:** - `task-execution-test-workflow` (type: F) — Ana test workflow'u (HTTP, Script, Timer Wait, StartFlow, GetInstanceData, Notification, TriggerTransition, SubProcess, GetInstances → completed). Human Task (tip 5) runtime tarafindan kaldirilacak gecici bir ozellik oldugu icin bu zincirden cikartildi. - `task-target-workflow` (type: F) — StartFlow / GetInstanceData / TriggerTransition / SubProcess / GetInstances icin hedef -- `extended-tasks-test-workflow` (type: F) — **Sadece Dapr** ile dis servis cagrisi: HTTP invoke, Service, Binding, PubSub (tek zincir; HTTP test: `api-tests/task-execution/task-execution.http` **Bolum 3**) +- `extended-tasks-test-workflow` (type: F) — **Sadece Dapr** ile dis servis cagrisi: HTTP invoke, Service, PubSub (tek zincir; HTTP test: `api-tests/task-execution/task-execution.http` **Bolum 3**). **Not:** Dapr Binding (tip 2) test kapsamindan cikarildi (bkz. dosya sonu **Test Edilmeyen Ozellikler** bolumu). **Amac:** Tum **runtime-ici** gorev tipleri ile **Dapr tabanli** gorev tiplerini ayri workflow'larda dogrular; task siralama (`onEntries` order), `context.Body.data`, scheduled transition (Timer Task tip 9), auto transition (Condition Task tip 8) ve Dapr bilesen YAML (`etc/dapr/components/`) ile uyumu kapsar. @@ -402,8 +402,9 @@ extended-tasks-test-workflow (F) │ ├── onEntries: script-task (Tasks/lifecycle-transitions/script-task.json) + InitExtendedTaskMapping.csx │ └── Transitions: auto-to-dapr-http → dapr-http-state │ -├── dapr-http-state → dapr-service-state → dapr-binding-state → dapr-pubsub-state -│ (sirasiyla type 1, 3, 2, 4 Dapr task'leri + Dapr*Mapping.csx) +├── dapr-http-state → dapr-service-state → dapr-pubsub-state +│ (sirasiyla type 1, 3, 4 Dapr task'leri + Dapr*Mapping.csx) +│ (Dapr Binding (tip 2) state'i ve task'i kaldirildi — bkz. Test Edilmeyen Ozellikler) │ └── completed-state (Final; pubsub sonrasi otomatik tamamlanma) ``` @@ -422,7 +423,7 @@ extended-tasks-test-workflow (F) | TriggerTransition Task (type:12) | Hedef instance + manuel gecis | trigger-transition-task + TriggerTransitionMapping (SetInstance + `manual-complete-target`) | | SubProcess Task (type:14) | Alt surec baslatma + hedef instance teyidi (`subprocessInstanceId` + `subprocessData` yanit ozeti + GET state/attributes) | subprocess-task + SubProcessMapping (SetBody parentInstanceId/source/note; OutputHandler `subprocessData` = `context.Body.data` alanlari) | | GetInstances Task (type:15) | Instance listesi | get-instances-task + GetInstancesMapping | -| Dapr HTTP (1) / Service (3) / Binding (2) / PubSub (4) | Sidecar + YAML | `extended-tasks-test-workflow` + dapr-*-task | +| Dapr HTTP (1) / Service (3) / PubSub (4) | Sidecar + YAML; **task gercekten calisti** kaniti (skill §6.4): HTTP/Service icin mocklab yanitindan donen `processId` parent attributes'a yazilir ve testte non-empty string assert edilir; PubSub icin runtime `context.Body.isSuccess` `published` olarak yazilir ve `true` assert edilir. **PubSub bileseni** task JSON'da `pubSubName: "{DaprPubSubName}"` placeholder; `DaprPubSubMapping.InputHandler` `GetConfigValue("DaprPubSubName")` ile Vault'tan okuyup `SetPubSubName(...)` ile override eder (varsayilan: `vnext-pubsub` Redis sistem bileseni). | `extended-tasks-test-workflow` + dapr-*-task; assertion'lar `ExtendedTasksWorkflowInstanceDataAssertions.AssertDaprChainCompleted` | | HttpTask / StartTask / GetInstanceDataTask cast | InputHandler | Ilgili mapping.csx | | context.Body.data | Task yaniti | OutputHandler'lar | | GetConfigValue | MocklabBaseUrl | HttpProcessMapping | @@ -443,7 +444,6 @@ extended-tasks-test-workflow (F) | Task | `get-instances-task` | 15 | Tasks/task-execution/task-execution-test-workflow/get-instances-task.json | | Task | `dapr-http-task` | 1 | Tasks/task-execution/extended-tasks-test-workflow/dapr-http-task.json | | Task | `dapr-service-task` | 3 | Tasks/task-execution/extended-tasks-test-workflow/dapr-service-task.json | -| Task | `dapr-binding-task` | 2 | Tasks/task-execution/extended-tasks-test-workflow/dapr-binding-task.json | | Task | `dapr-pubsub-task` | 4 | Tasks/task-execution/extended-tasks-test-workflow/dapr-pubsub-task.json | | Task | `script-task` (paylasimli) | 7 | Tasks/lifecycle-transitions/script-task.json | | CSX | `task-execution-test-workflow` mapping'leri | `Workflows/task-execution/src/task-execution-test-workflow/*.csx` | @@ -1002,8 +1002,8 @@ version-consistency-test-workflow (F, v2.0.0) — 4 state (ekstra review-state) | Dosya | Amac | |-------|------| -| `etc/dapr/components/test-binding.yaml` | Dapr output binding testleri (Grup 3, `extended-tasks-test-workflow`) | -| `etc/dapr/components/test-pubsub.yaml` | Dapr pub/sub testleri (Grup 3, `extended-tasks-test-workflow`) | +| `etc/dapr/components/test-binding.yaml` | (Su an **kullanilmiyor**; Dapr Binding test kapsamindan cikarildi — bkz. **Test Edilmeyen Ozellikler**) | +| `etc/dapr/components/test-pubsub.yaml` | (Su an **kullanilmiyor**; mockoon-dapr sidecar'inda mount edilse de execution-app sidecar'i yalnizca runtime'in yukledigi sistem pubsub bilesenlerini gorur. `extended-tasks-test-workflow` Dapr PubSub task'i Vault'taki `DaprPubSubName` (varsayilan: `vnext-pubsub`) bileseni uzerinden publish eder; `DaprPubSubMapping.csx` `InputHandler`'da `SetPubSubName` ile override edilir.) | | `etc/dapr/config.yaml` | Dapr genel konfigurasyon | ### Mockoon Mock Endpoint'leri @@ -1072,7 +1072,6 @@ Her grubun hangi vNext ozelliklerini test ettigini gosteren matris. **Gn** sutun | StartFlow Task (11) | | | X | | | | | | | | GetInstanceData Task (13) | | | X | | | | | | | | **Dapr HTTP Task (1)** | | | X | | | | | | | -| **Dapr Binding Task (2)** | | | X | | | | | | | | **Dapr Service Task (3)** | | | X | | | | | | | | **Dapr PubSub Task (4)** | | | X | | | | | | | | **Notification Task (10)** | | | X | | | | | | | @@ -1133,3 +1132,36 @@ Her grubun hangi vNext ozelliklerini test ettigini gosteren matris. **Gn** sutun 2. **platformOverrides**: `view-definition.schema.json` icinde `platformOverrides` alani tanimli degildir. Bu nedenle platform bazli view override testleri mevcut schema ile yapilamaz. 3. **Event transition (triggerType:3)**: Runtime dokumantasyonunda tanimlanan event-driven transition'lar icin test eklenmemistir. Bu, ileri asamada ayri bir senaryoda test edilebilir. + +--- + +## Test Edilmeyen Ozellikler + +Bu bolum, runtime tarafindan desteklenen ancak `vnext-example` integration test workflow'lari icinde **bilincli olarak test edilmeyen** ozellikleri listeler. Her madde icin neden test edilmediginin gerekcesi ve ileride etkin test eklenmek istenirse hangi yolun izlenecegine dair kisa bir not bulunur. + +### 1. Dapr Binding Task (tip 2) + +- **Onceki konum:** Grup 3 → `extended-tasks-test-workflow` icindeki `dapr-binding-state` (state, task ve mapping artik kaldirildi). +- **Kaldirilan dosyalar:** + - State: `core/Workflows/task-execution/extended-tasks-test-workflow.json` icindeki `dapr-binding-state` (ilgili `auto-to-dapr-binding` transition'i ile birlikte). Akis su an `dapr-service-state` → `dapr-pubsub-state` seklinde dogrudan ilerler. + - Task tanimi: `core/Tasks/task-execution/extended-tasks-test-workflow/dapr-binding-task.json` (silindi). + - Mapping: `core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprBindingMapping.csx` (silindi). +- **Korunan altyapi:** `etc/dapr/components/test-binding.yaml` Dapr bilesen tanimi dosyada **birakilmistir** ancak workflow tarafindan **artik referans edilmemektedir**; ileride yeniden test eklenmek istendiginde dogrudan kullanilabilir. +- **Neden test edilmiyor:** Dapr output binding davranisi, runtime'in stabil kapsami disinda kalan bir entegrasyondur ve mevcut integration test ortaminda guvenilir bir mocklab/Dapr kombinasyonu ile uctan uca dogrulanamamaktadir. Diger Dapr task tipleri (HTTP-1, Service-3, PubSub-4) `extended-tasks-test-workflow` icinde test edilmeye devam etmektedir. +- **Eklenmek istenirse:** Yeni bir `dapr-binding-state` + `dapr-binding-task` (tip 2) cifti `extended-tasks-test-workflow` icinde tekrar olusturulmalidir; Dapr tarafinda `test-binding.yaml` zaten hazirdir. Bu degisiklik mevcut `extended-tasks-test-workflow` zincirini ve C# integration test sozlesmesini (`taskResults.daprBinding`) yeniden gunceller. + +### 2. Human Task (tip 5) + +- **Onceki konum:** Daha onceki revizyonlarda Grup 3 (`task-execution-test-workflow`) Human Task ozelligini de kapsiyordu; bu zincir icindeki `human-task-state` ve `human-task` (tip 5) artik kaldirilmistir. +- **Neden test edilmiyor:** Human Task (tip 5) **runtime tarafindan kaldirilacak gecici bir ozellik** olarak isaretlenmistir. Bu nedenle: + - Bu ozellik **yeni test workflow'larina eklenmez**. + - Mevcut testlerde gecmiste kullanildiysa temizlenir (mevcut durumda `task-execution-test-workflow` ve `instance-management-test-workflow` icinde Human Task tipi referansi yoktur; yalnizca `instance-management` workflow'unda **state subType 6 (Human)** — Human Task task-tipi degil — final state alt tipi olarak korunmaktadir). +- **Iliskili ama farkli ozellik:** `instance-management-test-workflow` icindeki `human-state` (subType 6) Human **Task** tipi degil, **state alt tipi**dir. Bu state, runtime'in `subType` enum'unu test eder ve Human Task tip 5'in kaldirilma kararindan **etkilenmez**. +- **Eklenmek istenirse:** Runtime'da Human Task ozelligi kalici olarak destekleneceginin teyidi alindiginda; ayri bir `human-task-test-workflow` ve `human-task` (tip 5) JSON tanimi olusturulup Mocklab veya gercek bir human-task hub uctan uca testle entegre edilmelidir. Su an icin **eklenmemelidir**. + +### Ozet Tablo + +| Ozellik | Tip / Konum | Durum | Gerekce | +|---------|-------------|-------|---------| +| Dapr Binding Task | Task tipi 2, `extended-tasks-test-workflow` | **Kaldirildi (state + task + mapping silindi)** | Runtime kapsamindaki kararsizlik; mocklab/Dapr binding entegrasyonu uctan uca dogrulanamiyor | +| Human Task | Task tipi 5, daha once `task-execution-test-workflow` zincirinde | **Kaldirildi / yeni testlere eklenmez** | Runtime tarafindan kaldirilacak gecici ozellik; sadece state subType 6 (Human) korunur | diff --git a/tests/Core/Helpers/JsonElementAssertions.cs b/tests/Core/Helpers/JsonElementAssertions.cs index fc3be71..cd32739 100644 --- a/tests/Core/Helpers/JsonElementAssertions.cs +++ b/tests/Core/Helpers/JsonElementAssertions.cs @@ -91,4 +91,30 @@ public static void AssertNestedPropertyTrue( message ?? $"'{booleanPropertyName}' should be true under {string.Join(".", pathToParent)}." ); } + + /// + /// Nested object yolundaki string property'nin non-empty oldugunu dogrular. + /// Skill vnext-workflow-creation §6.4: task'in gercekten calistigi, yaniti parent attributes'a + /// non-empty deger olarak yansidiginda kanitlanir (sabit literal "completed = true" yetersiz). + /// + public static void AssertNestedPropertyNonEmptyString( + JsonElement root, + string[] pathToParent, + string stringPropertyName, + string? message = null + ) + { + Assert.True( + TryGetNestedObject(root, pathToParent, out var parent) + && parent.ValueKind == JsonValueKind.Object, + message + ?? $"Expected object at path '{string.Join(".", pathToParent)}'." + ); + AssertPropertyNonEmptyString( + parent, + stringPropertyName, + message + ?? $"'{stringPropertyName}' should be a non-empty string under {string.Join(".", pathToParent)}." + ); + } } diff --git a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs index f911383..94819c0 100644 --- a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs +++ b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs @@ -101,6 +101,16 @@ public static void AssertHappyPathCompleted(JsonElement attributes) /// /// extended-tasks-test-workflow sonunda Dapr zinciri için attributes kontrolleri. +/// Dapr Binding (tip 2) state ve task'i workflow'dan kaldirildi (bkz. integration-test-documentation.md +/// "Test Edilmeyen Ozellikler" bolumu); zincir su an HTTP -> Service -> PubSub seklinde ilerler. +/// +/// Skill vnext-workflow-creation §6.4 geregi her Dapr task'i icin sabit "completed = true" literal +/// bayraginin yaninda, task'in gercekten calistigini kanitlayan ek bir alan da assert edilir: +/// - daprHttp / daprService: mocklab yanitindan gelen processId (GUID) parent attributes'a +/// yazilir; non-empty string olmasi mocklab'a gercekten ulasildiginin kanitidir. +/// - daprPubSub: PubSub fire-and-forget oldugundan response body'sinde alan donmez; runtime +/// context.Body.isSuccess bayragini doner ve mapping bunu published olarak +/// parent attributes'a yazar; published == true task'in basarili publish ettiginin kanitidir. /// internal static class ExtendedTasksWorkflowInstanceDataAssertions { @@ -110,29 +120,48 @@ internal static class ExtendedTasksWorkflowInstanceDataAssertions public static void AssertDaprChainCompleted(JsonElement attributes) { JsonElementAssertions.AssertPropertyTrue(attributes, "initCompleted", ContractHint); + JsonElementAssertions.AssertNestedPropertyTrue( attributes, new[] { "taskResults", "daprHttp" }, "completed", ContractHint ); + // Skill §6.4: mapping bayragi "completed=true" literal; gercek task yanitinin (mocklab GUID) + // parent attributes'a yansidigi processId ile kanitlanir. + JsonElementAssertions.AssertNestedPropertyNonEmptyString( + attributes, + new[] { "taskResults", "daprHttp" }, + "processId", + $"daprHttp.processId mocklab yanitindan gelmeli {ContractHint}" + ); + JsonElementAssertions.AssertNestedPropertyTrue( attributes, new[] { "taskResults", "daprService" }, "completed", ContractHint ); + JsonElementAssertions.AssertNestedPropertyNonEmptyString( + attributes, + new[] { "taskResults", "daprService" }, + "processId", + $"daprService.processId mocklab yanitindan gelmeli {ContractHint}" + ); + JsonElementAssertions.AssertNestedPropertyTrue( attributes, - new[] { "taskResults", "daprBinding" }, + new[] { "taskResults", "daprPubSub" }, "completed", ContractHint ); + // PubSub fire-and-forget: runtime context.Body.isSuccess'i mapping parent attributes'a + // "published" olarak yazar; true olmasi PubSub broker'a gercekten gonderildigin kanitidir. JsonElementAssertions.AssertNestedPropertyTrue( attributes, new[] { "taskResults", "daprPubSub" }, - "completed", - ContractHint + "published", + $"daprPubSub.published runtime isSuccess bayragindan gelmeli {ContractHint}" ); } } diff --git a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs index 062d9ee..5d7d317 100644 --- a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs +++ b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs @@ -5,6 +5,16 @@ namespace Core.IntegrationTests.Tests.TaskExecutionTestWorkflow; +/* +!BUGS: +! 1. Dapr Pubsub'un context body'sinden dönen JSONELEMENT +! yapının json serialize işleminde hata veriyor, +! dispose olmuş / uygun olmayan durumdaki JsonElement + * System.InvalidOperationException: Operation is not valid due to the current state of the object. + * at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.GetMemberAndWriteJson(...) + * at BBT.Workflow.Tasks.Coordinator.TaskExecutionEngine.ExecuteCoreAsync(...):line 531 +*/ + /// /// api-tests/task-execution/task-execution.http B1–B3 ve /// doc/integration-test-documentation.md Task Execution grubu ile hizalı integration testler. From ae5453d374c5efc47a4f93b593e7d4cae69da08f Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Mon, 4 May 2026 14:21:25 +0300 Subject: [PATCH 12/24] instance-management test has done, needs a little refactor to make codes more clean. --- .cursor/skills/vnext-features/SKILL.md | 603 ++++++------------ .../instance-management-test-workflow.http | 15 +- .../postman-instance-management.json | 21 +- .../lifecycle-transitions-test-workflow.http | 6 +- .../postman-lifecycle-transitions.json | 2 +- api-tests/task-execution/task-execution.http | 84 ++- .../version-consistency-test.http | 98 +-- core.link.json | 2 +- .../account-details-input.json | 10 +- .../account-type-selection.json | 8 +- .../schema-data-confirm-transition.json | 15 +- .../schema-data/schema-data-master.json | 39 +- .../dapr-pubsub-task.json | 2 +- .../account-confirmation-view.json | 6 +- .../account-creation-view.json | 6 +- .../account-details-input-view.json | 6 +- ...ount-details-submit-confirmation-view.json | 6 +- .../account-opening-success-view.json | 6 +- ...ount-type-selection-confirmation-view.json | 6 +- .../account-type-selection-view.json | 33 +- .../final-confirmation-popup-view.json | 6 +- .../policy-validation-view.json | 6 +- .../subflow-test/child-workflow-view.json | 7 +- .../grandchild-workflow-view.json | 6 +- .../subflow-test/parent-workflow-view.json | 7 +- .../account-opening-workflow.json | 74 +-- .../src/TriggerScheduledPaymentsMapping.csx | 2 +- .../collection-object-test-workflow.json | 70 +- .../error-boundary-test-workflow.json | 58 +- core/Workflows/flow-types/core-flow-test.json | 22 +- .../flow-types/subprocess-flow-test.json | 22 +- ...ance-management-test-workflow.diagram.json | 39 +- .../instance-management-test-workflow.json | 70 +- .../lifecycle-transitions-test-workflow.json | 94 +-- .../payment-notification-subflow.json | 32 +- core/Workflows/payments/payment-process.json | 70 +- .../payments/scheduled-payments-workflow.json | 70 +- .../src/SendPaymentNotificationSmsMapping.csx | 6 +- .../SendPaymentPushNotificationMapping.csx | 18 +- .../schema-data-test-workflow.json | 46 +- .../smoke-runtime-check-workflow.json | 20 - .../subflow-orchestration-child.json | 46 +- .../subflow-orchestration-grandchild.json | 30 +- .../subflow-orchestration-parent.json | 42 +- .../busy-subprocess-workflow.json | 30 +- .../subflow-test/subflow-view-test-child.json | 46 +- .../subflow-view-test-grandchild.json | 38 +- .../subflow-view-test-parent.json | 46 +- .../extended-tasks-test-workflow.json | 46 +- .../DaprPubSubMapping.csx | 109 +--- .../task-execution-test-workflow.json | 86 +-- .../task-execution/task-target-workflow.json | 22 +- .../version-consistency-test-workflow-v2.json | 38 +- .../version-consistency-test-workflow.json | 30 +- ...view-function-extension-test-workflow.json | 42 +- doc/integration-test-documentation.md | 18 +- ...xt Example Runtime.postman_collection.json | 6 +- tests/Core/Helpers/InstanceListJson.cs | 107 ++++ tests/Core/Helpers/JsonElementAssertions.cs | 8 +- tests/Core/Helpers/StateFunctionJson.cs | 2 +- ...nstanceManagementInstanceDataAssertions.cs | 80 +++ .../InstanceManagementTestWorkflowTests.cs | 490 ++++++++++++++ ...LifecyclePassPathInstanceDataAssertions.cs | 4 +- .../LifecycleTransitionsTestWorkflowTests.cs | 31 +- .../TaskExecutionInstanceDataAssertions.cs | 54 +- .../TaskExecutionTestWorkflowTests.cs | 81 +-- 66 files changed, 1239 insertions(+), 2012 deletions(-) create mode 100644 tests/Core/Helpers/InstanceListJson.cs create mode 100644 tests/Core/Tests/instance-management-test-workflow/InstanceManagementInstanceDataAssertions.cs create mode 100644 tests/Core/Tests/instance-management-test-workflow/InstanceManagementTestWorkflowTests.cs diff --git a/.cursor/skills/vnext-features/SKILL.md b/.cursor/skills/vnext-features/SKILL.md index fcc6f47..eb03387 100644 --- a/.cursor/skills/vnext-features/SKILL.md +++ b/.cursor/skills/vnext-features/SKILL.md @@ -1,74 +1,72 @@ --- name: vnext-features -description: Scope is repo-wide domain knowledge (not bound to a single workspace). vNext Runtime platform referansı. Workflow, state, transition, task, schema, view, function ve extension kavramlarını, JSON tanım yapılarını ve C# mapping/rule pattern'lerini kapsar. vNext bileşenleri oluşturulurken veya düzenlenirken bu skill'i kullanın. Trigger this skill whenever vNext workflow/task/schema/view/function/extension JSON or `.csx` mapping/rule files are being created or edited anywhere in the monorepo (or in target vnext domain repositories). +description: Repo-wide domain knowledge (not bound to a single workspace). vNext Runtime platform reference. Covers workflow, state, transition, task, schema, view, function and extension concepts, JSON definition structures, and C# mapping/rule patterns. Use this skill when creating or editing vNext components. Trigger whenever vNext workflow/task/schema/view/function/extension JSON or `.csx` mapping/rule files are created or edited anywhere in the monorepo (or in target vNext domain repositories). --- # vNext Skill -> **Scope:** Repo-wide domain reference. vNext bileşenleri (workflow, task, schema, view, function, extension JSON ve `.csx` mapping/rule dosyaları) ile çalışırken her workspace'te tetiklenir; tek bir `apps/*` veya `packages/*` ile sınırlı değildir. +> **Scope:** Repo-wide domain reference. Applies in every workspace when working with vNext components (workflow, task, schema, view, function, extension JSON and `.csx` mapping/rule files); not limited to a single `apps/*` or `packages/*`. -Bu dosya, vNext Runtime'ı hızlı ama eksiksiz sayılabilecek bir çerçevede anlamak için hazırlanmış kompakt bir referanstır. Amaç, vNext ile çalışan birinin platformun ana modelini, JSON tanımlarını, desteklenen yapılarını ve genişleme noktalarını tek yerden kavramasıdır. +This file is a compact reference for understanding vNext Runtime quickly yet completely. Its goal is to give one place where someone working with vNext can grasp the core model, JSON definitions, supported structures, and extension points. -## 1. vNext Nedir? +## 1. What is vNext? -vNext, iş akışlarını JSON ile tanımlanan, versiyonlanan ve runtime'da state machine olarak çalışan workflow'lar üzerinden yöneten bir platformdur. +vNext is a platform where processes are modeled as workflows defined and versioned in JSON and executed as state machines at runtime. -Temel yaklaşım: +Core ideas: -- Süreçler birer `workflow/flow` olarak tanımlanır. -- Workflow içindeki ilerleme `state` ve `transition` yapıları ile kontrol edilir. -- İş yapan parça `task`'tır. -- Veri kontratı `schema` ile tanımlanır. -- UI veya istemci gösterimi `view` ile verilir. -- Okuma/erişim amaçlı runtime endpoint'leri `function` ile sunulur. -- Response zenginleştirme `extension` ile yapılır. -- Tekrarlı kod yerine `reference` ve `version strategy` kullanılır. +- Processes are defined as `workflow`/flow artifacts. +- Progress through a workflow is controlled by `state` and `transition`. +- Units of work are `task`s. +- Data contracts are defined with `schema`. +- Client/UI presentation uses `view`. +- Runtime read/access endpoints are surfaced as `function`. +- Response enrichment uses `extension`. +- Reuse favors `reference` and version strategy rather than duplication. -Kısacası vNext: +So vNext brings together: -- süreç modelleme, -- süreç orkestrasyonu, -- entegrasyon, -- stateful runtime, -- versiyonlu tanım yönetimi +- process modeling, +- orchestration, +- integrations, +- a stateful runtime, +- versioned definition management, -işlerini tek çatı altında toplar. +under one umbrella. -## 2. Zihinsel Model +## 2. Mental Model -vNext'i doğru anlamanın en kısa yolu şu akışı bilmektir: +The shortest path to reasoning about vNext is this lifecycle: -1. Bir workflow JSON'u yayınlanır. -2. Runtime bu workflow'un bir `instance`'ını başlatır. -3. Instance bir state'te durur. -4. Kullanıcı, event, timer veya otomatik kural bir transition tetikler. -5. Transition öncesi/sonrası task'lar çalışır. -6. Veri schema kurallarına göre taşınır ve doğrulanır. -7. Gerekirse subflow/subprocess başlatılır. -8. Runtime state'i değiştirir, view ve function çıktıları buna göre güncellenir. +1. A workflow JSON is published. +2. The runtime starts an `instance` of that workflow. +3. The instance sits in a state. +4. A user, event, timer, or automatic rule triggers a transition. +5. Tasks run before/around the transition. +6. Data is carried and validated per schema rules. +7. Optionally subflows/subprocesses are started. +8. The runtime changes state; view and function outputs follow. -Bu yüzden vNext'te ana odak "sayfa" değil, "durumdan duruma ilerleyen süreç"tir. +So vNext is about **moving from state to state**, not about “screens” alone. -## 3. Ana Elemanlar +## 3. Building Blocks ### 3.1 Workflow / Flow -Workflow, sürecin ana tanımıdır. Zorunlu olarak: +A workflow is the primary process definition. It must include: - `type` - `startTransition` - `states` -alanlarını içerir. - -Desteklenen workflow tipleri: +Supported workflow kinds: - `C`: Core - `F`: Flow - `S`: SubFlow - `P`: SubProcess -Sık kullanılan ek alanlar: +Common extra fields: - `labels` - `timeout` @@ -80,25 +78,25 @@ Sık kullanılan ek alanlar: - `schema` - `errorBoundary` -Ne zaman hangisi: +When to use which: -- `F`: ana iş akışı -- `S`: parent ile bağlı çalışan alt akış -- `P`: bağımsız, fire-and-forget benzeri alt süreç -- `C`: sistemsel/çekirdek tanımlar +- `F`: primary business flow +- `S`: child flow tightly coupled to parent +- `P`: independent, fire-and-forget–style subprocess +- `C`: system/core definitions ### 3.2 State -State, instance'ın o an bulunduğu adımdır. +State is where the instance is at a moment in time. -State tipleri: +State types: - `1`: Initial - `2`: Intermediate - `3`: Finish - `4`: SubFlow -State alt tipleri: +State subtypes: - `0`: None - `1`: Success @@ -108,7 +106,7 @@ State alt tipleri: - `5`: Busy - `6`: Human -Bir state içinde tipik olarak şunlar bulunur: +Typical state fields: - `key` - `stateType` @@ -121,23 +119,23 @@ Bir state içinde tipik olarak şunlar bulunur: - `subFlow` - `errorBoundary` -Önemli not: +Notes: -- `SubFlow` state'i parent-child ilişki kurar. -- Runtime, alt akışlarda `effectiveState` mantığı ile dışarıya daha anlamlı durum gösterebilir. +- A `SubFlow` state binds parent and child hierarchically. +- The runtime may expose richer external state via `effectiveState` in nested flows. ### 3.3 Transition -Transition, state değişimini başlatan tanımdır. +Transitions define valid state moves. -Ana alanlar: +Main fields: - `key` - `from` - `target` - `triggerType` -Opsiyonel alanlar: +Optional fields: - `timer` - `rule` @@ -148,38 +146,38 @@ Opsiyonel alanlar: - `mapping` - `onExecutionTasks` -Trigger tipleri: +Trigger types: - `0`: Manual - `1`: Automatic - `2`: Scheduled - `3`: Event -Önemli davranışlar: +Important behaviour: -- `startTransition` başlangıç geçişidir, `from` içermez. -- `availableIn`, ortak transition'ların birden fazla state'te kullanılmasını sağlar. -- `target: "$self"` deseni, özellikle subflow sonrası parent data güncelleme gibi senaryolarda kullanılır. -- Geçiş payload'ı mapping yoksa doğrudan instance data'ya merge edilir. +- `startTransition` is the entry transition (no `from`). +- `availableIn` pins shared transitions across multiple states. +- `target: "$self"` is common for scenarios like refreshing parent data after subflow completion. +- If there is no mapping, transition payloads merge straight into instance data. ### 3.4 Task -Task, gerçek işi yapan birimdir. vNext'te entegrasyonun ve işlem yürütmenin temel taşıdır. +Tasks perform real work—they are integration and execution primitives. -Task'lar şu yerlerde çalışabilir: +Tasks can run: - state `onEntries` - state `onExits` - transition `onExecutionTasks` -- function tanımları -- extension tanımları +- function definitions +- extension definitions -Çalışma sırası: +Ordering: -- Aynı `order`: paralel -- Farklı `order`: küçükten büyüğe sıralı +- same `order`: parallel +- different `order`: ascending sequential -Ana task aileleri: +Families: - `Http` - `DaprService` @@ -187,17 +185,17 @@ Ana task aileleri: - `Script` - `Trigger` - `GetInstances` -- `Condition` (sistemsel) -- `Timer` (sistemsel) +- `Condition` (generated) +- `Timer` (generated) -Trigger görev ailesinde öne çıkanlar: +Notables in the Trigger family: - `StartTask` - `DirectTriggerTask` - `GetInstanceDataTask` - `SubProcessTask` -Task response tarafında standart olarak şu tip bilgiler bulunur: +Typical response metadata: - `Data` - `StatusCode` @@ -208,13 +206,13 @@ Task response tarafında standart olarak şu tip bilgiler bulunur: - `ExecutionDurationMs` - `TaskType` -Bu yapı sayesinde task'lar sadece çağrı yapmak için değil, workflow orchestration için de kullanılır. +So tasks are not only adapters—they orchestrate workflows. ### 3.5 Schema -Schema, veri kontratıdır. Hem workflow verisini hem task/function/view gibi tanım tiplerini standardize eder. +Schema is the contract. It applies to workflow data and typed definitions (tasks, functions, views, etc.). -Schema tanımı tipik olarak şu alanları taşır: +Common envelope fields: - `key` - `version` @@ -224,7 +222,7 @@ Schema tanımı tipik olarak şu alanları taşır: - `tags` - `attributes` -`attributes.type` destekleri: +`attributes.type` values include: - `workflow` - `task` @@ -234,266 +232,90 @@ Schema tanımı tipik olarak şu alanları taşır: - `extension` - `headers` -Schema motoru JSON Schema Draft 2020-12 tabanlıdır. - -Desteklenen ana JSON Schema özellikleri: - -- primitive tipler: `string`, `number`, `integer`, `boolean`, `array`, `object`, `null` -- string doğrulamaları: `minLength`, `maxLength`, `pattern`, `format` -- formatlar: `email`, `uri`, `date`, `date-time`, `time`, `uuid`, `ipv4`, `ipv6` -- sayısal doğrulamalar: `minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum`, `multipleOf` -- array alanları: `items`, `minItems`, `maxItems`, `uniqueItems` -- object alanları: `properties`, `required`, `additionalProperties` -- koşullu yapılar: `if/then/else`, `oneOf`, `anyOf`, `allOf` +Engines use JSON Schema Draft 2020-12 primitives and keywords (constraints, combinators). -Flow seviyesinde `master schema` kullanılabilir. Bu, instance data'nın kalıcı kontratıdır. Yan ürün geliştirirken en önemli referans çoğu zaman budur. +At flow scope, `master schema` is usually the authoritative instance contract. -Ek olarak: - -- property bazlı görünürlük/authorization kurguları desteklenir. -- transition bazlı schema ile kullanıcı aksiyon payload'ı ayrı doğrulanabilir. +Schemas also carry field visibility/authorization notions and transition-scoped validation. ### 3.6 View -View, istemcinin ne göstereceğini tanımlar. UI kodu olmak zorunda değildir; render edilebilir bir temsil tanımıdır. +View describes what clients render—not necessarily imperative UI—structured renderable payloads. -Ana alanlar: +Important fields: -- `key` -- `flow` -- `domain` -- `version` -- `type` -- `content` -- `display` -- `labels` -- `platformOverrides` +- key, flow, domain, version (envelope), plus `type`, `content`, `display`, labels, overrides. -Desteklenen view tipleri: +Representations include JSON/HTML/Markdown/DeepLink/etc. -- `Json` -- `Html` -- `Markdown` -- `DeepLink` -- `Http` -- `URN` - -Display modları: - -- `full-page` -- `popup` -- `bottom-sheet` -- `top-sheet` -- `drawer` -- `inline` - -Platform override destekleri: - -- `web` -- `ios` -- `android` +Modes like `full-page`, `popup`, `bottom-sheet`, etc. -View seçimi: +Selections can target state transitions, platforms, locales. -- state view -- transition view -- rule-based view selection -- platform override -- dil/label seçimi - -Yan ürün geliştirirken view'dan alınması gereken mesaj: - -- vNext UI'yı dikte etmez, -- ancak UI için yapılandırılabilir bir gösterim katmanı sunar. +Key idea: **vNext does not dictate UX**—it emits configurable presentation payloads. ### 3.7 Function -Function, workflow instance'ı okumak veya workflow ile etkileşmek için sistemsel API yüzeyidir. - -Temel built-in function'lar: - -- `state` -- `data` -- `view` - -Ne sağlarlar: +Functions are first-class endpoints for inspecting or interacting without leaking orchestration internals. -- `state`: mevcut state, mümkün transition'lar, subflow korelasyonları -- `data`: instance data, etag, extension datası -- `view`: state/transition bazlı view içeriği +Built-ins include `state`, `data`, `view`. -Function'lar: +Benefits: -- istemcinin runtime iç yapıyı bilmeden veri almasını sağlar, -- long-polling ve ETag gibi senaryoları destekler, -- authorization ve role bazlı görünürlüğe uyum sağlar. +- clients stay decoupled from orchestration internals +- polling/ETags +- aligns with authorization/roles ### 3.8 Extension -Extension, instance response'unu zenginleştiren yardımcı bileşendir. - -Function ile farkı: - -- Function dış endpoint sunar. -- Extension dış endpoint sunmaz. -- Extension çıktısı `extensions` alanında response'a eklenir. - -Extension tipleri: +Extensions enrich runtime responses—not external endpoints themselves. -- `1`: Global -- `2`: GlobalAndRequested -- `3`: DefinedFlows -- `4`: DefinedFlowAndRequested +Compared to Functions: -Scope tipleri: +- Function = dedicated endpoint path +- Extension = merged into responses under `extensions` -- `1`: GetInstance -- `2`: GetAllInstances / history benzeri okuma uçları -- `3`: Everywhere +Types/scopes classify global vs selective attachment. -Kullanım amacı: - -- harici bilgi ekleme -- hesaplanmış veri ekleme -- profil, limit, referans bilgi, session gibi zenginleştirme +Purpose: enrichment (profiles, aggregates, lookups, computed fields). ### 3.9 Mapping -Mapping, vNext'in veri dönüştürme katmanıdır. Task'a giden veriyi ve task'tan dönen veriyi runtime context ile bağlar. - -Ana mapping türleri: - -- input mapping -- output mapping -- transition mapping -- timer mapping -- condition mapping -- subflow mapping -- subprocess mapping - -Kritik interface'ler: - -- `IMapping` -- `ITimerMapping` -- `ISubProcessMapping` -- `ISubFlowMapping` -- `IConditionMapping` -- `ITransitionMapping` +Mappings adapt data flowing into/out of tasks and transitions (`IMapping`, `ITimerMapping`, conditional/transition/subflow/subprocess mappings, etc.) -Script tarafında tipik çalışma context'i: +Script context exposes body, headers, query, routing, instance and definition metadata, responses, metadata. -- request body -- headers -- query parameters -- route values -- instance data -- workflow definition -- transition definition -- task response'ları -- metadata - -Script motoru Roslyn/C# tabanlıdır. Native C# ve encode edilmiş script kullanımı desteklenir. +Engines use Roslyn/C# plus encoded scripts where applicable. ### 3.10 Error Boundary -Error handling hiyerarşik çalışır: - -- task seviyesi -- state seviyesi -- global workflow seviyesi - -Ana aksiyonlar: +Handled hierarchically: task/state/global. -- `Abort` -- `Retry` -- `Rollback` -- `Ignore` -- `Notify` -- `Log` +Policies include abort/retry/rollback/ignore/log/notify with retry/backoff settings. -Retry policy ile: +Important for modelling failure paths—not just happy paths. -- `maxRetries` -- `initialDelay` -- `backoffType` -- `backoffMultiplier` -- `maxDelay` -- `useJitter` +### 3.11 Reference & Versioning -tanımlanabilir. +Definitions link via `{ key, domain, version, flow }` refs across tasks, views, schemas, flows, subprocesses, extensions. -Yan ürün geliştirirken bu önemli çünkü vNext sadece "happy path" değil, hata ve retry akışını da model seviyesinde taşır. +System flow tokens include `sys-flows`, `sys-views`, `sys-functions`, `sys-tasks`, `sys-extensions`, `sys-schemas`. -### 3.11 Reference ve Versioning +Semver strategies keep runtime separate from authoring. -vNext'te tekrar yerine referans kullanılır. Tanımlar birbirine doğrudan gömülmek yerine version'lı reference ile bağlanır. +### 3.12 Instance & Persistence -Tam referans yapısı: - -```json -{ - "key": "customer-lookup", - "domain": "crm", - "version": "1.2.0", - "flow": "sys-tasks" -} -``` +Runtime executes instances, not mere definitions. -Kullanıldığı yerler: +Important instance concepts: identifiers, versioning, etag, tags, payload, metadata, persisted history. -- task referansı -- subflow referansı -- function referansı -- extension referansı -- schema referansı -- view referansı +Architecturally: master instance records plus auditable histories; optionally domain-split storage. -Sistemsel akışlar: - -- `sys-flows` -- `sys-views` -- `sys-functions` -- `sys-tasks` -- `sys-extensions` -- `sys-schemas` - -Versioning yaklaşımı: - -- semver -- explicit version referansı -- version strategy ile latest/major/minor benzeri seçim - -Bu sayede runtime ve tanım yönetimi ayrışır. - -### 3.12 Instance ve Persistence - -Runtime'da çalışan şey tanım değil, `instance`'tır. - -Instance tarafında tipik kavramlar: - -- `id` -- `key` -- `flow` -- `domain` -- `flowVersion` -- `etag` -- `tags` -- `attributes` -- `metadata` -- `data` -- `extensions` - -Persistence modeli özetle: - -- workflow instance kayıtları master data tarafında tutulur, -- transition/task/action/job geçmişi ayrı tablolarda izlenebilir, -- domain bazlı veritabanı ayrımı bulunur. - -Bu yapı vNext'i tek bir workflow engine olmaktan çıkarıp çok domain'li enterprise runtime haline getirir. +--- -## 4. JSON Nasıl Oluşturulur? +## 4. How Definitions Are Structured -vNext'te çoğu tanım ortak bir zarf yapısı kullanır: +Envelope: ```json { @@ -507,15 +329,15 @@ vNext'te çoğu tanım ortak bir zarf yapısı kullanır: } ``` -Pratikte dikkat edilmesi gerekenler: +Practical reminders: -- `key`: benzersiz ve okunabilir olsun -- `version`: semver olsun -- `domain`: sahiplik alanını anlatsın -- `flow`: bunun hangi sistem koleksiyonuna ait olduğunu göstersin -- `attributes`: asıl işi burada tanımlarsın +- `key`: readable unique id +- `version`: semver +- `domain`: ownership boundary +- `flow`: which system collection owns the artifact +- `attributes`: the actual definition body -## 5. Minimal JSON İskeletleri +## 5. Minimal JSON Skeletons ### 5.1 Minimal Workflow @@ -558,7 +380,7 @@ Pratikte dikkat edilmesi gerekenler: } ``` -### 5.2 Transition İçinde Task Kullanımı +### 5.2 Tasks Inside a Transition ```json { @@ -650,128 +472,63 @@ Pratikte dikkat edilmesi gerekenler: } ``` -## 6. vNext Hangi Yapıları ve Feature'ları Destekliyor? - -### 6.1 Süreç Modelleme - -- version'lı workflow -- state machine -- start transition -- finish state -- manual/auto/event/scheduled transition -- shared transition -- cancel flow -- timeout -- subflow -- subprocess -- parent-child correlation - -### 6.2 Orkestrasyon ve Entegrasyon - -- HTTP çağrıları -- Dapr service invocation -- Dapr pub/sub -- script execution -- başka workflow başlatma -- doğrudan transition tetikleme -- instance data okuma -- instance listeleme / filtreleme -- timer ve condition tabanlı otomasyon - -### 6.3 Veri ve Doğrulama - -- JSON Schema Draft 2020-12 -- master schema -- transition schema -- headers schema -- field-level visibility -- camelCase veri kullanım pratiği -- mapping ile custom payload transform - -### 6.4 UI ve Client Entegrasyonu - -- state view -- transition view -- rule-based view selection -- Json/Html/Markdown/DeepLink/Http/URN view tipleri -- display modları -- platform override -- localization/labels - -### 6.5 Runtime API Yetenekleri - -- state polling -- data fetch -- view fetch -- permissions/authorize endpoint'leri -- ETag / conditional fetch -- extension ile enriched response - -### 6.6 Kurumsal Özellikler - -- semver ve version strategy -- domain bazlı ayrışma -- ayrı veritabanı / schema yapıları -- audit/history izleme -- retry/backoff/jitter -- role/queryRole bazlı yetkilendirme -- hata boundary hiyerarşisi - -## 7. Yan Ürün Geliştirecek Biri İçin Yol Haritası - -vNext üzerinde yan ürün geliştirirken genelde şu giriş noktalarından biri kullanılır: - -- workflow JSON üretici/editörü -- schema/view validator -- runtime client SDK -- instance monitor dashboard -- transition execution paneli -- workflow diff/version karşılaştırıcı -- task kütüphanesi yöneticisi -- extension/function katalog aracı - -En kritik doğrular: - -- Süreci state machine olarak düşün. -- Kalıcı veri kontratını `master schema` üzerinden kur. -- Tekrarlı gömme yerine `reference` kullan. -- UI'ı workflow'dan değil, `view + state + schema` üçlüsünden türet. -- Entegrasyon mantığını `task + mapping` ile taşı. -- Okuma API'leri için `function`, response zenginleştirme için `extension` kullan. - -## 8. Kısa Tasarım Kuralları - -- Workflow'ları küçük ve anlamlı tut. -- State isimleri business meaning taşısın. -- Transition isimleri aksiyon fiili gibi olsun. -- Task'ları tekrar kullanılabilir tasarla. -- Aynı concern'ü function ve extension arasında karıştırma. -- Schema'yı gerçeğin kaynağı kabul et. -- Subflow ile subprocess farkını net koru: biri bağlı, biri bağımsız. -- Error boundary tanımını en yakın seviyede çöz, global'i fallback olarak bırak. - -## 9. Sonuç - -vNext, JSON tabanlı, versiyonlu, stateful bir workflow runtime platformudur. Ana yapı taşları: - -- workflow -- state -- transition -- task -- schema -- view -- function -- extension -- mapping -- reference/versioning -- instance/persistence -- error boundary - -Bir kişi bu dosyayı baz alarak: - -- vNext'in neyi çözdüğünü, -- workflow JSON'un nasıl kurulduğunu, -- hangi feature'ların desteklendiğini, -- nereden genişletme yapılacağını - -anlayıp vNext için araç, entegrasyon, editör, SDK veya izleme ürünü geliştirmeye başlayabilir. +## 6. Major Feature Areas + +Summaries: + +### 6.1 Process Modeling +Versioned workflows, state machines, start/finish semantics, transitions, cancellations, timeouts, hierarchies. + +### 6.2 Orchestration & Integration +HTTP, Dapr invocation/pubsub, scripts, spawning flows, triggering transitions, querying instance data/lists, timers/conditions. + +### 6.3 Data & Validation +JSON Schema drafts, master/transition schemas, headers schema, camelCase payloads, mappings. + +### 6.4 UI & Clients +Stateful views, selectors, locales, overrides. + +### 6.5 Runtime APIs +Polling, data/view fetch, authorize endpoints, etag support, enrichment. + +### 6.6 Enterprise Capabilities +Version strategies, domains, auditing, backoff, roles/queryRoles, nested error boundaries. + +## 7. Product Roadmap Hints for Downstream Builders + +Likely tooling surfaces: + +- workflow editors/generators, +- validators for schema/view parity, +- client SDKs, +- monitoring dashboards / transition explorers, +- diff/version inspectors, +- task catalogue maintainers. + +Guiding truths: + +1. Treat everything as a state machine narrative. +2. Anchor durable contracts on master schema. +3. Prefer references over cloning definitions. +4. Derive UX from views + schemas + states, not workflows alone. +5. Place integration semantics in tasks + mappings. +6. Consume data via Functions; decorate via Extensions. + +## 8. Design Rules of Thumb + +- Keep flows purposeful and cohesive. +- State names encode business milestones. +- Transition names behave like verbs. +- Reusable tasks beat one-off payloads. +- Do not confuse extension vs function responsibilities. +- Treat schema as the source of truth. +- Keep subprocess vs subflow distinctions crisp. +- Define error boundaries as locally as feasible with global fallback. + +## 9. Closing + +vNext is a JSON-defined, versioned, stateful workflow runtime. Pillars: + +`workflow • state • transition • task • schema • view • function • extension • mapping • reference/versioning • instance persistence • error boundary` + +Use this primer to bootstrap tooling for modeling, authoring, integrating, deploying, observing, or governing vNext-based systems. diff --git a/api-tests/instance-management/instance-management-test-workflow.http b/api-tests/instance-management/instance-management-test-workflow.http index ccf37ef..bdf5d41 100644 --- a/api-tests/instance-management/instance-management-test-workflow.http +++ b/api-tests/instance-management/instance-management-test-workflow.http @@ -74,14 +74,17 @@ Content-Type: application/json # TEST 2: List / filter / sort / pagination ############################################################################### -### Filter by category (finance) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances?filter=attributes.category%20eq%20%27finance%27 +### Filter by category (finance) — GraphQL / JSON filter (recommended) +### Decoded: filter={"attributes":{"category":{"eq":"finance"}}} +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances?filter=%7B%22attributes%22%3A%7B%22category%22%3A%7B%22eq%22%3A%22finance%22%7D%7D%7D -### Filter by state key -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances?filter=state%20eq%20%27active-state%27 +### Filter by state key — GraphQL / JSON filter +### Decoded: filter={"currentState":{"eq":"active-state"}} +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances?filter=%7B%22currentState%22%3A%7B%22eq%22%3A%22active-state%22%7D%7D -### Filter by status (active) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances?filter=status%20eq%20%27A%27 +### Filter by status (active) — GraphQL / JSON filter +### Decoded: filter={"status":{"eq":"A"}} +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances?filter=%7B%22status%22%3A%7B%22eq%22%3A%22A%22%7D%7D ### Sort descending by createdAt GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances?sort=-createdAt diff --git a/api-tests/instance-management/postman-instance-management.json b/api-tests/instance-management/postman-instance-management.json index 498c9d5..dae796d 100644 --- a/api-tests/instance-management/postman-instance-management.json +++ b/api-tests/instance-management/postman-instance-management.json @@ -306,12 +306,13 @@ "name": "TEST 2: List / filter / sort / pagination", "item": [ { - "name": "Filter by category (finance)", + "name": "Filter by category (finance) — GraphQL / JSON filter", "request": { "method": "GET", "header": [], + "description": "GraphQL / JSON filter format (recommended by vnext-runtime docs).\nDecoded: filter={\"attributes\":{\"category\":{\"eq\":\"finance\"}}}", "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances?filter=attributes.category%20eq%20%27finance%27", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances?filter=%7B%22attributes%22%3A%7B%22category%22%3A%7B%22eq%22%3A%22finance%22%7D%7D%7D", "host": [ "{{baseUrl}}" ], @@ -326,7 +327,7 @@ "query": [ { "key": "filter", - "value": "attributes.category eq 'finance'" + "value": "{\"attributes\":{\"category\":{\"eq\":\"finance\"}}}" } ] } @@ -334,12 +335,13 @@ "response": [] }, { - "name": "Filter by state key", + "name": "Filter by state key — GraphQL / JSON filter", "request": { "method": "GET", "header": [], + "description": "GraphQL / JSON filter format (recommended by vnext-runtime docs).\nDecoded: filter={\"currentState\":{\"eq\":\"active-state\"}}", "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances?filter=state%20eq%20%27active-state%27", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances?filter=%7B%22currentState%22%3A%7B%22eq%22%3A%22active-state%22%7D%7D", "host": [ "{{baseUrl}}" ], @@ -354,7 +356,7 @@ "query": [ { "key": "filter", - "value": "state eq 'active-state'" + "value": "{\"currentState\":{\"eq\":\"active-state\"}}" } ] } @@ -362,12 +364,13 @@ "response": [] }, { - "name": "Filter by status (active)", + "name": "Filter by status (active) — GraphQL / JSON filter", "request": { "method": "GET", "header": [], + "description": "GraphQL / JSON filter format (recommended by vnext-runtime docs).\nDecoded: filter={\"status\":{\"eq\":\"A\"}}", "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances?filter=status%20eq%20%27A%27", + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances?filter=%7B%22status%22%3A%7B%22eq%22%3A%22A%22%7D%7D", "host": [ "{{baseUrl}}" ], @@ -382,7 +385,7 @@ "query": [ { "key": "filter", - "value": "status eq 'A'" + "value": "{\"status\":{\"eq\":\"A\"}}" } ] } diff --git a/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http b/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http index 55c4a71..c2155af 100644 --- a/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http +++ b/api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http @@ -30,7 +30,7 @@ Content-Type: application/json GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{passInstanceId}}/functions/state ### Adim 3: Manuel Transition - Move to Processing (ITransitionMapping + transition roles) -### transition roles: yalnizca test-operator allow. State fonksiyonu (GET .../functions/state) transitions[] listesini role'e gore filtreler (v0.0.39+). PATCH cagrisinda ayni kuralla zorunlu red resmi dokümanda taahhüt edilmez. +### transition roles: test-operator allow only. State function (GET .../functions/state) filters transitions[] by role (v0.0.39+). PATCH is not formally guaranteed to be denied by the same rule in docs. PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{passInstanceId}}/transitions/move-to-processing?sync=true Content-Type: application/json role: test-operator @@ -335,8 +335,8 @@ role: test-viewer ### ============================================================ ### TEST: QueryRoles ve Transition Roles -### State-level queryRoles artık pre-complete-state üzerinde (processing-state auto geçişli, gözlemlenemez). -### Akış: move -> auto-passed -> cancel-schedule-manually -> pre-complete -> authorize (processor OK, viewer deny) +### State-level queryRoles is on pre-complete-state (processing-state auto-only, not observable). +### Flow: move -> auto-passed -> cancel-schedule-manually -> pre-complete -> authorize (processor OK, viewer deny) ### ============================================================ ### Adim 1: Workflow Baslat (Roles Test) diff --git a/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json b/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json index 33af4a8..bddb9f5 100644 --- a/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json +++ b/api-tests/lifecycle-transitions/postman-lifecycle-transitions.json @@ -1051,7 +1051,7 @@ " pm.collectionVariables.set(\"scheduleCancelInstanceId\", jsonData.id);", " console.log(\"scheduleCancelInstanceId set to: \" + jsonData.id);", "}", - "pm.test('ScheduleCancel: start yanıtında id', function () {", + "pm.test('ScheduleCancel: id in start response', function () {", " pm.expect(jsonData && jsonData.id).to.be.ok;", "});" ] diff --git a/api-tests/task-execution/task-execution.http b/api-tests/task-execution/task-execution.http index 665b151..db33346 100644 --- a/api-tests/task-execution/task-execution.http +++ b/api-tests/task-execution/task-execution.http @@ -7,26 +7,25 @@ @mocklabBaseUrl = http://localhost:3002 ############################################################################### -# vNext Task execution — tek dosya (Postman koleksiyonu ile ayni bolum yapisi) +# vNext task execution — single file (same section layout as Postman collection) # -# BOLUM 1 — Ana zincir (task-execution-test-workflow): happy path -# BOLUM 2 — Hedef workflow (task-target-workflow): happy path -# BOLUM 3 — Dapr zinciri (extended-tasks-test-workflow): happy path +# SECTION 1 — Main chain (task-execution-test-workflow): happy path +# SECTION 2 — Target workflow (task-target-workflow): happy path +# SECTION 3 — Dapr chain (extended-tasks-test-workflow): happy path ############################################################################### ############################################################################### -# BOLUM 1 — Happy path: Ana task execution zinciri -# HTTP -> Script -> Timer Wait (Timer Task tip 9, scheduled transition 3 sn) -# -> Start Flow (11) -> Get Instance Data (13) -# -> Notification (10, mapping.type G) -> TriggerTransition (12) -> SubProcess (14) -# -> GetInstances (15) -> Completed (auto) -# Condition Task (tip 8) tum auto transition rule'larinda (AlwaysTrueRule) dolayli olarak uretilir. -# NOT: Human Task (tip 5) runtime tarafindan kaldirilacak gecici bir ozellik oldugu icin bu -# zincirden cikartildi; happy path manuel onay olmadan dogrudan completed-state'e ulasir. +# SECTION 1 — Happy path: main task execution chain +# HTTP → Script → Timer wait (Timer Task type 9, scheduled transition ~3s) +# → Start Flow (11) → Get Instance Data (13) +# → Notification (10, mapping.type G) → TriggerTransition (12) → SubProcess (14) +# → GetInstances (15) → Completed (auto) +# Condition Task (type 8) is produced indirectly for auto rules (AlwaysTrueRule). +# NOTE: Human Task (type 5) is deprecated — removed from this chain; happy path completes without manual approval. # Requires: MocklabBaseUrl config, Mocklab seeded (POST /api/test/process) ############################################################################### -### B1 — Adim 1: Ana workflow baslat (sync zincir completed-state'e ulasir) +### B1 — Step 1: Start main workflow (sync runs chain to completed-state) # @name createTaskExecInstance POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json @@ -39,43 +38,42 @@ Content-Type: application/json @taskExecInstanceId = {{createTaskExecInstance.response.body.$.id}} -### B1 — Adim 2: Durum kontrol (completed-state, status: C) +### B1 — Step 2: State check (completed-state, status: C) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/state -### B1 — Adim 3: Instance data (notification / trigger / subprocess / getInstances bayraklari) -# Beklenen: testId, httpTaskCompleted, scriptProcessed, -# timerStartedAt (TimerStartMapping anligi - timer-wait-state'e girildigi an), -# timerExpectedSeconds = 3 (TimerScheduleMapping bekleme suresi), -# startFlowCompleted, startedInstanceId (StartTask sonucu hedef workflow id), -# subprocessInstanceId (SubProcessTask sonucu hedef workflow id), -# subprocessData (SubProcessTask context.Body.data anlık kopyası: id/instanceId/state vb.), +### B1 — Step 3: Instance data (notification / trigger / subprocess / getInstances flags) +# Expected: testId, httpTaskCompleted, scriptProcessed, +# timerStartedAt (TimerStartMapping when entering timer-wait-state), +# timerExpectedSeconds = 3 (TimerScheduleMapping duration), +# startFlowCompleted, startedInstanceId (StartTask → target workflow id), +# subprocessInstanceId (SubProcessTask → target workflow id), +# subprocessData (point-in-time SubProcessTask context.Body.data: id/instanceId/state, …), # getInstanceDataCompleted, taskResults.triggerTransition.completed, # taskResults.subprocess.completed, taskResults.getInstances.completed -# Not: completed-state'e ulasilana kadar gecen toplam sure >= 3 sn olmali; aksi halde -# scheduled transition'in Timer Task'i devre disi olabilir. +# Total elapsed until completed-state should be ≥ ~3s; otherwise the scheduled Timer path may not be exercised. GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{taskExecInstanceId}}/functions/data -### B1 — Adim 3a: StartTask ile baslatilan hedef instance functions/state (target-completed olmali) -# startedInstanceId'yi B1 Adim 3 cikisindan kopyalayip asagidaki degiskene yapistir. -# (Postman koleksiyonu bunu otomatik degisken olarak set eder.) +### B1 — Step 3a: functions/state on instance started via StartTask (expect target-completed) +# Paste startedInstanceId from B1 Step 3 into the variable below. +# (Postman collection sets this automatically.) @taskExecStartedTargetId = REPLACE_WITH_startedInstanceId GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{taskExecStartedTargetId}}/functions/state -### B1 — Adim 3b: SubProcessTask ile acilan hedef instance functions/state -# Subprocess fire-and-forget'tir; state target-initial (Active) veya target-completed olabilir. +### B1 — Step 3b: functions/state on subprocess target instance (SubProcessTask) +# Subprocess is fire-and-forget; state may be target-initial (active) or target-completed. @taskExecSubprocessId = REPLACE_WITH_subprocessInstanceId GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{taskExecSubprocessId}}/functions/state -### B1 — Adim 3c: SubProcessTask ile acilan hedef instance attributes -# Beklenen: source = "task-execution-test", parentInstanceId = ana workflow instance id, -# note alani SubProcessMapping InputHandler'da SetBody ile yazilmali. +### B1 — Step 3c: Attributes on subprocess target instance (SubProcessTask) +# Expected: source = "task-execution-test", parentInstanceId = main workflow instance id, +# note set via SubProcessMapping InputHandler SetBody. GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{taskExecSubprocessId}} ############################################################################### -# BOLUM 2 — Happy path: Hedef workflow (task-target-workflow) bagimsiz baslatma +# SECTION 2 — Happy path: start target workflow (task-target-workflow) standalone ############################################################################### -### B2 — Adim 1: Hedef workflow baslat +### B2 — Step 1: Start target workflow # @name createTargetInstance POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/start?sync=true Content-Type: application/json @@ -88,22 +86,21 @@ Content-Type: application/json @targetInstanceId = {{createTargetInstance.response.body.$.id}} -### B2 — Adim 2: Hedefde manuel gecis (DirectTriggerTask ile ayni anahtar: manual-complete-target) +### B2 — Step 2: Manual transition on target (same key as DirectTriggerTask: manual-complete-target) PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{targetInstanceId}}/transitions/manual-complete-target Content-Type: application/json {} -### B2 — Adim 3: Hedef workflow durumu (target-completed olmali) +### B2 — Step 3: Target workflow state (expect target-completed) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{targetWorkflow}}/instances/{{targetInstanceId}}/functions/state ############################################################################### -# BOLUM 3 — Happy path: Dapr task zinciri (extended-tasks-test-workflow, sadece dis servis) -# Dapr HTTP (1) -> Dapr Service (3) -> Dapr Binding (2) -> -# Dapr PubSub (4) -> completed-state (otomatik) +# SECTION 3 — Happy path: Dapr-only chain (extended-tasks-test-workflow) +# Dapr HTTP (1) → Dapr Service (3) → Dapr PubSub (4) → completed-state (auto) ############################################################################### -### B3 — Adim 1: Workflow baslat (sync - tam zincir) +### B3 — Step 1: Start workflow (sync — full chain) # @name createExtendedInstance POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{extendedWorkflow}}/instances/start?sync=true Content-Type: application/json @@ -118,15 +115,14 @@ Content-Type: application/json @extendedInstanceId = {{createExtendedInstance.response.body.$.id}} -### B3 — Adim 2: Durum kontrol - completed-state olmali +### B3 — Step 2: State check — expect completed-state GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{extendedWorkflow}}/instances/{{extendedInstanceId}}/functions/state -### B3 — Adim 3: Instance - Dapr task sonuclari +### B3 — Step 3: Instance payload — Dapr task results GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{extendedWorkflow}}/instances/{{extendedInstanceId}} -### B3 — Beklenen sonuclar (yorum): +### B3 — Expected (reference): ### - initCompleted: true ### - taskResults.daprHttp.completed: true ### - taskResults.daprService.completed: true -### - taskResults.daprBinding.completed: true -### - taskResults.daprPubSub.completed: true +### - taskResults.daprPubSub.completed / published proofs (see integration tests §6.4) diff --git a/api-tests/version-consistency/version-consistency-test.http b/api-tests/version-consistency/version-consistency-test.http index aa3f53a..65822f5 100644 --- a/api-tests/version-consistency/version-consistency-test.http +++ b/api-tests/version-consistency/version-consistency-test.http @@ -6,35 +6,35 @@ ############################################################################### # VERSION CONSISTENCY TEST # -# Bu test, workflow versiyonu degistiginde mevcut instance'larin kendi -# versiyonlarinin akisindan devam ettigini dogrular. +# Validates that when the workflow version changes, existing instances keep +# following the flow pinned to their own definition version. # -# ONKOŞUL: Sadece v1.0.0 publish edilmis olmali. -# v2.0.0 HENUZ publish edilmemis olmali. +# PREREQUISITE: Only v1.0.0 should be published. +# v2.0.0 must NOT yet be published. # -# ADIMLAR: -# 1. v1.0.0 workflow'u publish et (version-consistency-test-workflow.json) -# 2. Instance A'yi baslat (v1.0.0 uzerinde calisacak) -# 3. Instance A'yi processing-state'e tasra (auto ile gider) -# 4. v2.0.0 workflow'u publish et (version-consistency-test-workflow-v2.json) -# 5. Instance B'yi baslat (v2.0.0 uzerinde calisacak) -# 6. Instance B'yi processing-state'e birak -# 7. Instance A: complete-processing → completed-state (v1 yolu, review YOK) -# 8. Instance B: complete-processing → review-state (v2 yolu) +# STEPS: +# 1. Publish v1.0.0 workflow (version-consistency-test-workflow.json) +# 2. Start instance A (runs on v1.0.0) +# 3. Move instance A to processing-state (auto) +# 4. Publish v2.0.0 workflow (version-consistency-test-workflow-v2.json) +# 5. Start instance B (runs on v2.0.0) +# 6. Leave instance B on processing-state +# 7. Instance A: complete-processing → completed-state (v1 path, NO review) +# 8. Instance B: complete-processing → review-state (v2 path) # 9. Instance B: approve-review → completed-state -# 10. Her iki instance'in datasini karsilastir +# 10. Compare data on both instances ############################################################################### ############################################################################### -# ADIM 1: v1.0.0 PUBLISH EDILMIS OLMALI -# → version-consistency-test-workflow.json dosyasini publish edin +# STEP 1: v1.0.0 MUST BE PUBLISHED +# → Publish version-consistency-test-workflow.json ############################################################################### ############################################################################### -# ADIM 2: Instance A baslatma (v1.0.0 uzerinde) +# STEP 2: Start instance A (v1.0.0 line) ############################################################################### -### Instance A - Baslat (v1.0.0 akisinda calisacak) +### Instance A - Start (pinned to v1.0.0) # @name createInstanceA POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json @@ -50,23 +50,23 @@ Content-Type: application/json @instanceAId = {{createInstanceA.response.body.$.id}} ############################################################################### -# ADIM 3: Instance A state kontrolu (processing-state'te olmali) +# STEP 3: Instance A state check (should be processing-state) ############################################################################### -### Instance A - State kontrolu (processing-state bekleniyor) +### Instance A - State check (expect processing-state) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceAId}}/functions/state ############################################################################### -# ADIM 4: v2.0.0 PUBLISH ET -# → version-consistency-test-workflow-v2.json dosyasini publish edin -# Instance A hala processing-state'te bekliyor. +# STEP 4: Publish v2.0.0 +# → Publish version-consistency-test-workflow-v2.json +# Instance A remains on processing-state. ############################################################################### ############################################################################### -# ADIM 5: Instance B baslatma (v2.0.0 uzerinde calisacak) +# STEP 5: Start instance B (v2.0.0 line) ############################################################################### -### Instance B - Baslat (v2.0.0 akisinda calisacak) +### Instance B - Start (pinned to v2.0.0) # @name createInstanceB POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json @@ -82,78 +82,78 @@ Content-Type: application/json @instanceBId = {{createInstanceB.response.body.$.id}} ############################################################################### -# ADIM 6: Instance B state kontrolu (processing-state'te olmali) +# STEP 6: Instance B state check (should be processing-state) ############################################################################### -### Instance B - State kontrolu (processing-state bekleniyor) +### Instance B - State check (expect processing-state) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/functions/state ############################################################################### -# ADIM 7: Instance A'yi tamamla -# complete-processing → completed-state (v1 yolu, review-state YOK) +# STEP 7: Complete instance A +# complete-processing → completed-state (v1 path, NO review-state) ############################################################################### -### Instance A - complete-processing (v1: dogrudan completed-state'e gitmeli) +### Instance A - complete-processing (v1: should land on completed-state) PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceAId}}/transitions/complete-processing?sync=true Content-Type: application/json {} -### Instance A - Final state kontrolu (completed-state bekleniyor) +### Instance A - Final state (expect completed-state) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceAId}}/functions/state -### Instance A - Data kontrolu -# BEKLENEN: v1Completed=true, reviewExecuted OLMAMALI +### Instance A - Data check +# EXPECTED: v1Completed=true; reviewExecuted must NOT appear GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceAId}}/functions/data ############################################################################### -# ADIM 8: Instance B'yi processing'den ilerlet -# complete-processing → review-state (v2 yolu, ekstra state) +# STEP 8: Advance instance B from processing +# complete-processing → review-state (v2 extra state) ############################################################################### -### Instance B - complete-processing (v2: review-state'e gitmeli) +### Instance B - complete-processing (v2: expect review-state) PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/transitions/complete-processing?sync=true Content-Type: application/json {} -### Instance B - State kontrolu (review-state bekleniyor) +### Instance B - State check (expect review-state) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/functions/state -### Instance B - Data kontrolu (reviewExecuted=true olmali) +### Instance B - Data check (reviewExecuted=true) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/functions/data ############################################################################### -# ADIM 9: Instance B review'dan completed'a +# STEP 9: Instance B review → completed ############################################################################### -### Instance B - approve-review (v2: completed-state'e gitmeli) +### Instance B - approve-review (v2: completed-state) PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/transitions/approve-review?sync=true Content-Type: application/json {} -### Instance B - Final state kontrolu (completed-state bekleniyor) +### Instance B - Final state (expect completed-state) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/functions/state -### Instance B - Final data kontrolu -# BEKLENEN: reviewExecuted=true, v2Completed=true, v1Completed OLMAMALI +### Instance B - Final data +# EXPECTED: reviewExecuted=true, v2Completed=true; v1Completed must NOT be set GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/functions/data ############################################################################### -# DOGRULAMA OZETI +# VALIDATION SUMMARY # # Instance A (v1.0.0): # - State: completed-state # - Data: v1Completed=true, completedByVersion="1.0.0" -# - reviewExecuted OLMAMALI (v1'de review-state yok) +# - reviewExecuted must be absent (no review-state on v1) # # Instance B (v2.0.0): # - State: completed-state # - Data: reviewExecuted=true, v2Completed=true, completedByVersion="2.0.0" -# - v1Completed OLMAMALI (v2 yolunu izledi) +# - v1Completed must NOT be set (followed v2 path) # -# BASARI KRITERI: -# v2 publish edildikten sonra bile Instance A, v1 akisini takip etti -# ve review-state'e ugramadan dogrudan completed'a gitti. +# SUCCESS: +# After publishing v2, instance A still completes on the v1 path and reaches +# completed without passing through review-state. ############################################################################### diff --git a/core.link.json b/core.link.json index b99b734..3566c5d 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-05-04T09:07:01.988Z" + "importedAt": "2026-05-04T10:35:23.871Z" } \ No newline at end of file diff --git a/core/Schemas/account-opening/account-details-input.json b/core/Schemas/account-opening/account-details-input.json index 05e5861..1a9f9ad 100644 --- a/core/Schemas/account-opening/account-details-input.json +++ b/core/Schemas/account-opening/account-details-input.json @@ -30,7 +30,7 @@ "description": "Name/alias for the account", "minLength": 1, "maxLength": 50, - "pattern": "^[a-zA-ZğüşıöçĞÜŞİÖÇ0-9\\s\\-_]+$" + "pattern": "^[a-zA-Z0-9\\s\\-_]+$" }, "currency": { "type": "string", @@ -79,19 +79,19 @@ "oneOf": [ { "const": "personal-banking", - "description": "Kişisel Bankacılık - Personal Banking" + "description": "Personal Banking" }, { "const": "salary-account", - "description": "Maaş Hesabı - Salary Account" + "description": "Salary Account" }, { "const": "savings", - "description": "Tasarruf - Savings" + "description": "Savings" }, { "const": "business-expenses", - "description": "İş Giderleri - Business Expenses" + "description": "Business Expenses" } ] }, diff --git a/core/Schemas/account-opening/account-type-selection.json b/core/Schemas/account-opening/account-type-selection.json index d4220d4..c4380e9 100644 --- a/core/Schemas/account-opening/account-type-selection.json +++ b/core/Schemas/account-opening/account-type-selection.json @@ -29,19 +29,19 @@ "oneOf": [ { "const": "demand-deposit", - "description": "Vadesiz Hesap - Demand Deposit Account" + "description": "Demand Deposit Account" }, { "const": "time-deposit", - "description": "Vadeli Hesap - Time Deposit Account" + "description": "Time Deposit Account" }, { "const": "investment-account", - "description": "Fonlu Hesap - Investment Account" + "description": "Investment Account" }, { "const": "savings-account", - "description": "Tasarruf Hesabı - Savings Account" + "description": "Savings Account" } ] } diff --git a/core/Schemas/schema-data/schema-data-confirm-transition.json b/core/Schemas/schema-data/schema-data-confirm-transition.json index 428efae..82985b5 100644 --- a/core/Schemas/schema-data/schema-data-confirm-transition.json +++ b/core/Schemas/schema-data/schema-data-confirm-transition.json @@ -4,17 +4,17 @@ "domain": "core", "flow": "sys-schemas", "flowVersion": "1.0.0", - "tags": ["integration-test", "schema-data", "transition-schema"], + "tags": [ + "integration-test", + "schema-data", + "transition-schema" + ], "attributes": { "type": "workflow", "labels": [ { "language": "en-US", "label": "Confirm Transition Schema" - }, - { - "language": "tr-TR", - "label": "Onay Gecisi Semasi" } ], "schema": { @@ -31,7 +31,10 @@ "minLength": 1 } }, - "required": ["confirmed", "confirmedBy"], + "required": [ + "confirmed", + "confirmedBy" + ], "additionalProperties": true } } diff --git a/core/Schemas/schema-data/schema-data-master.json b/core/Schemas/schema-data/schema-data-master.json index aef4852..cd68273 100644 --- a/core/Schemas/schema-data/schema-data-master.json +++ b/core/Schemas/schema-data/schema-data-master.json @@ -4,17 +4,18 @@ "domain": "core", "flow": "sys-schemas", "flowVersion": "1.0.0", - "tags": ["integration-test", "schema-data", "master-schema", "field-roles"], + "tags": [ + "integration-test", + "schema-data", + "master-schema", + "field-roles" + ], "attributes": { "type": "workflow", "labels": [ { "language": "en-US", "label": "Schema Data Test Master Schema" - }, - { - "language": "tr-TR", - "label": "Schema Data Test Ana Semasi" } ], "schema": { @@ -37,7 +38,11 @@ }, "currency": { "type": "string", - "enum": ["TRY", "USD", "EUR"] + "enum": [ + "TRY", + "USD", + "EUR" + ] }, "status": { "type": "string" @@ -45,14 +50,23 @@ "internalNote": { "type": "string", "roles": [ - { "role": "admin", "grant": "allow" }, - { "role": "customer", "grant": "deny" } + { + "role": "admin", + "grant": "allow" + }, + { + "role": "customer", + "grant": "deny" + } ] }, "auditLog": { "type": "string", "roles": [ - { "role": "auditor", "grant": "allow" } + { + "role": "auditor", + "grant": "allow" + } ] }, "updatedAt": { @@ -60,7 +74,12 @@ "format": "date-time" } }, - "required": ["orderId", "customerName", "amount", "currency"], + "required": [ + "orderId", + "customerName", + "amount", + "currency" + ], "additionalProperties": true } } diff --git a/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-pubsub-task.json b/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-pubsub-task.json index c660fd6..c939f6e 100644 --- a/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-pubsub-task.json +++ b/core/Tasks/task-execution/extended-tasks-test-workflow/dapr-pubsub-task.json @@ -8,7 +8,7 @@ "attributes": { "type": "4", "config": { - "pubSubName": "test-pubsub", + "pubSubName": "vnext-pubsub", "topic": "test-topic", "data": { "eventType": "IntegrationTest", diff --git a/core/Views/account-opening/account-confirmation-view.json b/core/Views/account-opening/account-confirmation-view.json index b2d691f..71bfb70 100644 --- a/core/Views/account-opening/account-confirmation-view.json +++ b/core/Views/account-opening/account-confirmation-view.json @@ -16,13 +16,9 @@ { "language": "en-US", "label": "Confirm Account Details" - }, - { - "language": "tr-TR", - "label": "Hesap Bilgilerini Onaylayın" } ], "display": "full-page", - "content": "{\"type\":\"confirmation-summary\",\"title\":{\"en-US\":\"Review Your Account Details\",\"tr-TR\":\"Hesap Bilgilerinizi Gözden Geçirin\"},\"description\":{\"en-US\":\"Please review all the information below before proceeding with your account opening.\",\"tr-TR\":\"Hesap açılışınıza devam etmeden önce lütfen aşağıdaki tüm bilgileri gözden geçirin.\"},\"sections\":[{\"key\":\"account-type\",\"title\":{\"en-US\":\"Account Type\",\"tr-TR\":\"Hesap Türü\"},\"fields\":[{\"key\":\"selectedAccountType\",\"label\":{\"en-US\":\"Selected Account Type\",\"tr-TR\":\"Seçilen Hesap Türü\"},\"value\":\"{{accountType.accountType}}\",\"displayValue\":{\"demand-deposit\":{\"en-US\":\"Demand Deposit Account\",\"tr-TR\":\"Vadesiz Mevduat Hesabı\"}}}]},{\"key\":\"account-details\",\"title\":{\"en-US\":\"Account Details\",\"tr-TR\":\"Hesap Detayları\"},\"fields\":[{\"key\":\"accountName\",\"label\":{\"en-US\":\"Account Name\",\"tr-TR\":\"Hesap Adı\"},\"value\":\"{{accountDetails.accountName}}\"},{\"key\":\"currency\",\"label\":{\"en-US\":\"Currency\",\"tr-TR\":\"Para Birimi\"},\"value\":\"{{accountDetails.currency}}\"},{\"key\":\"branchCode\",\"label\":{\"en-US\":\"Branch\",\"tr-TR\":\"Şube\"},\"value\":\"{{accountDetails.branchCode}}\"},{\"key\":\"initialDeposit\",\"label\":{\"en-US\":\"Initial Deposit\",\"tr-TR\":\"İlk Yatırım\"},\"value\":\"{{accountDetails.initialDeposit}}\",\"format\":\"currency\"},{\"key\":\"accountPurpose\",\"label\":{\"en-US\":\"Account Purpose\",\"tr-TR\":\"Hesap Amacı\"},\"value\":\"{{accountDetails.accountPurpose}}\",\"displayValue\":{\"personal\":{\"en-US\":\"Personal Use\",\"tr-TR\":\"Kişisel Kullanım\"},\"business\":{\"en-US\":\"Business Use\",\"tr-TR\":\"İş Kullanımı\"},\"savings\":{\"en-US\":\"Savings\",\"tr-TR\":\"Tasarruf\"}}}]},{\"key\":\"notifications\",\"title\":{\"en-US\":\"Notification Preferences\",\"tr-TR\":\"Bildirim Tercihleri\"},\"fields\":[{\"key\":\"smsNotifications\",\"label\":{\"en-US\":\"SMS Notifications\",\"tr-TR\":\"SMS Bildirimleri\"},\"value\":\"{{accountDetails.notifications.smsNotifications}}\",\"format\":\"boolean\"},{\"key\":\"emailNotifications\",\"label\":{\"en-US\":\"Email Notifications\",\"tr-TR\":\"E-posta Bildirimleri\"},\"value\":\"{{accountDetails.notifications.emailNotifications}}\",\"format\":\"boolean\"},{\"key\":\"pushNotifications\",\"label\":{\"en-US\":\"Push Notifications\",\"tr-TR\":\"Anlık Bildirimler\"},\"value\":\"{{accountDetails.notifications.pushNotifications}}\",\"format\":\"boolean\"}]}],\"buttons\":[{\"key\":\"confirm-account-opening\",\"label\":{\"en-US\":\"Confirm & Open Account\",\"tr-TR\":\"Onayla & Hesap Aç\"},\"style\":\"primary\",\"action\":\"submit\",\"transition\":\"confirm-account-opening\"},{\"key\":\"edit\",\"label\":{\"en-US\":\"Edit Details\",\"tr-TR\":\"Bilgileri Düzenle\"},\"style\":\"secondary\",\"action\":\"back\"}]}" + "content": "{\"type\":\"confirmation-summary\",\"title\":{\"en-US\":\"Review Your Account Details\"},\"description\":{\"en-US\":\"Please review all the information below before proceeding with your account opening.\"},\"sections\":[{\"key\":\"account-type\",\"title\":{\"en-US\":\"Account Type\"},\"fields\":[{\"key\":\"selectedAccountType\",\"label\":{\"en-US\":\"Selected Account Type\"},\"value\":\"{{accountType.accountType}}\",\"displayValue\":{\"demand-deposit\":{\"en-US\":\"Demand Deposit Account\"}}}]},{\"key\":\"account-details\",\"title\":{\"en-US\":\"Account Details\"},\"fields\":[{\"key\":\"accountName\",\"label\":{\"en-US\":\"Account Name\"},\"value\":\"{{accountDetails.accountName}}\"},{\"key\":\"currency\",\"label\":{\"en-US\":\"Currency\"},\"value\":\"{{accountDetails.currency}}\"},{\"key\":\"branchCode\",\"label\":{\"en-US\":\"Branch\"},\"value\":\"{{accountDetails.branchCode}}\"},{\"key\":\"initialDeposit\",\"label\":{\"en-US\":\"Initial Deposit\"},\"value\":\"{{accountDetails.initialDeposit}}\",\"format\":\"currency\"},{\"key\":\"accountPurpose\",\"label\":{\"en-US\":\"Account Purpose\"},\"value\":\"{{accountDetails.accountPurpose}}\",\"displayValue\":{\"personal\":{\"en-US\":\"Personal Use\"},\"business\":{\"en-US\":\"Business Use\"},\"savings\":{\"en-US\":\"Savings\"}}}]},{\"key\":\"notifications\",\"title\":{\"en-US\":\"Notification Preferences\"},\"fields\":[{\"key\":\"smsNotifications\",\"label\":{\"en-US\":\"SMS Notifications\"},\"value\":\"{{accountDetails.notifications.smsNotifications}}\",\"format\":\"boolean\"},{\"key\":\"emailNotifications\",\"label\":{\"en-US\":\"Email Notifications\"},\"value\":\"{{accountDetails.notifications.emailNotifications}}\",\"format\":\"boolean\"},{\"key\":\"pushNotifications\",\"label\":{\"en-US\":\"Push Notifications\"},\"value\":\"{{accountDetails.notifications.pushNotifications}}\",\"format\":\"boolean\"}]}],\"buttons\":[{\"key\":\"confirm-account-opening\",\"label\":{\"en-US\":\"Confirm & Open Account\"},\"style\":\"primary\",\"action\":\"submit\",\"transition\":\"confirm-account-opening\"},{\"key\":\"edit\",\"label\":{\"en-US\":\"Edit Details\"},\"style\":\"secondary\",\"action\":\"back\"}]}" } } diff --git a/core/Views/account-opening/account-creation-view.json b/core/Views/account-opening/account-creation-view.json index 771144b..584a834 100644 --- a/core/Views/account-opening/account-creation-view.json +++ b/core/Views/account-opening/account-creation-view.json @@ -16,13 +16,9 @@ { "language": "en-US", "label": "Creating Your Account" - }, - { - "language": "tr-TR", - "label": "Hesabınız Oluşturuluyor" } ], "display": "full-page", - "content": "{\"type\":\"loading-screen\",\"title\":{\"en-US\":\"Creating Your Bank Account\",\"tr-TR\":\"Banka Hesabınız Oluşturuluyor\"},\"description\":{\"en-US\":\"We are setting up your new account in our core banking system. Please wait while we complete this process.\",\"tr-TR\":\"Yeni hesabınızı ana bankacılık sistemimizde oluşturuyoruz. Bu işlemi tamamlarken lütfen bekleyin.\"},\"loadingIndicator\":true,\"showProgress\":true,\"steps\":[{\"key\":\"account-setup\",\"label\":{\"en-US\":\"Account Setup\",\"tr-TR\":\"Hesap Kurulumu\"}},{\"key\":\"number-generation\",\"label\":{\"en-US\":\"Account Number Generation\",\"tr-TR\":\"Hesap Numarası Oluşturma\"}},{\"key\":\"service-activation\",\"label\":{\"en-US\":\"Service Activation\",\"tr-TR\":\"Hizmet Aktivasyonu\"}}]}" + "content": "{\"type\":\"loading-screen\",\"title\":{\"en-US\":\"Creating Your Bank Account\"},\"description\":{\"en-US\":\"We are setting up your new account in our core banking system. Please wait while we complete this process.\"},\"loadingIndicator\":true,\"showProgress\":true,\"steps\":[{\"key\":\"account-setup\",\"label\":{\"en-US\":\"Account Setup\"}},{\"key\":\"number-generation\",\"label\":{\"en-US\":\"Account Number Generation\"}},{\"key\":\"service-activation\",\"label\":{\"en-US\":\"Service Activation\"}}]}" } } diff --git a/core/Views/account-opening/account-details-input-view.json b/core/Views/account-opening/account-details-input-view.json index 4d17949..d86ddc1 100644 --- a/core/Views/account-opening/account-details-input-view.json +++ b/core/Views/account-opening/account-details-input-view.json @@ -16,13 +16,9 @@ { "language": "en-US", "label": "Account Details" - }, - { - "language": "tr-TR", - "label": "Hesap Bilgileri" } ], "display": "full-page", - "content": "{\"type\":\"form\",\"title\":{\"en-US\":\"Enter Account Details\",\"tr-TR\":\"Hesap Bilgilerini Girin\"},\"description\":{\"en-US\":\"Please provide the following information to set up your account.\",\"tr-TR\":\"Hesabınızı oluşturmak için lütfen aşağıdaki bilgileri sağlayın.\"},\"sections\":[{\"key\":\"basic-info\",\"title\":{\"en-US\":\"Basic Information\",\"tr-TR\":\"Temel Bilgiler\"},\"fields\":[{\"key\":\"accountName\",\"type\":\"text\",\"label\":{\"en-US\":\"Account Name\",\"tr-TR\":\"Hesap Adı\"},\"placeholder\":{\"en-US\":\"Enter account name\",\"tr-TR\":\"Hesap adını girin\"},\"required\":true,\"maxLength\":50},{\"key\":\"currency\",\"type\":\"select\",\"label\":{\"en-US\":\"Currency\",\"tr-TR\":\"Para Birimi\"},\"required\":true,\"options\":[{\"value\":\"TRY\",\"label\":\"Turkish Lira (₺)\"},{\"value\":\"USD\",\"label\":\"US Dollar ($)\"},{\"value\":\"EUR\",\"label\":\"Euro (€)\"}]},{\"key\":\"branchCode\",\"type\":\"select\",\"label\":{\"en-US\":\"Branch\",\"tr-TR\":\"Şube\"},\"required\":true,\"options\":[{\"value\":\"001\",\"label\":\"Main Branch - Istanbul\"},{\"value\":\"002\",\"label\":\"Ankara Branch\"},{\"value\":\"003\",\"label\":\"Izmir Branch\"}]}]},{\"key\":\"initial-deposit\",\"title\":{\"en-US\":\"Initial Deposit\",\"tr-TR\":\"İlk Yatırım\"},\"fields\":[{\"key\":\"initialDeposit\",\"type\":\"number\",\"label\":{\"en-US\":\"Initial Deposit Amount\",\"tr-TR\":\"İlk Yatırım Tutarı\"},\"placeholder\":{\"en-US\":\"0.00\",\"tr-TR\":\"0,00\"},\"required\":false,\"min\":0,\"step\":0.01,\"currency\":true},{\"key\":\"accountPurpose\",\"type\":\"select\",\"label\":{\"en-US\":\"Account Purpose\",\"tr-TR\":\"Hesap Amacı\"},\"required\":true,\"options\":[{\"value\":\"personal\",\"label\":{\"en-US\":\"Personal Use\",\"tr-TR\":\"Kişisel Kullanım\"}},{\"value\":\"business\",\"label\":{\"en-US\":\"Business Use\",\"tr-TR\":\"İş Kullanımı\"}},{\"value\":\"savings\",\"label\":{\"en-US\":\"Savings\",\"tr-TR\":\"Tasarruf\"}}]}]},{\"key\":\"notifications\",\"title\":{\"en-US\":\"Notification Preferences\",\"tr-TR\":\"Bildirim Tercihleri\"},\"fields\":[{\"key\":\"smsNotifications\",\"type\":\"checkbox\",\"label\":{\"en-US\":\"SMS Notifications\",\"tr-TR\":\"SMS Bildirimleri\"},\"description\":{\"en-US\":\"Receive transaction alerts via SMS\",\"tr-TR\":\"İşlem uyarılarını SMS ile alın\"},\"defaultValue\":true},{\"key\":\"emailNotifications\",\"type\":\"checkbox\",\"label\":{\"en-US\":\"Email Notifications\",\"tr-TR\":\"E-posta Bildirimleri\"},\"description\":{\"en-US\":\"Receive account statements via email\",\"tr-TR\":\"Hesap ekstrelerini e-posta ile alın\"},\"defaultValue\":true},{\"key\":\"pushNotifications\",\"type\":\"checkbox\",\"label\":{\"en-US\":\"Push Notifications\",\"tr-TR\":\"Anlık Bildirimler\"},\"description\":{\"en-US\":\"Receive real-time notifications on your mobile device\",\"tr-TR\":\"Mobil cihazınızda gerçek zamanlı bildirimler alın\"},\"defaultValue\":true}]}],\"buttons\":[{\"key\":\"submit-account-details\",\"label\":{\"en-US\":\"Continue\",\"tr-TR\":\"Devam Et\"},\"style\":\"primary\",\"action\":\"submit\",\"transition\":\"submit-account-details\",\"requiresValidForm\":true},{\"key\":\"back\",\"label\":{\"en-US\":\"Back\",\"tr-TR\":\"Geri\"},\"style\":\"secondary\",\"action\":\"back\"}]}" + "content": "{\"type\":\"form\",\"title\":{\"en-US\":\"Enter Account Details\"},\"description\":{\"en-US\":\"Please provide the following information to set up your account.\"},\"sections\":[{\"key\":\"basic-info\",\"title\":{\"en-US\":\"Basic Information\"},\"fields\":[{\"key\":\"accountName\",\"type\":\"text\",\"label\":{\"en-US\":\"Account Name\"},\"placeholder\":{\"en-US\":\"Enter account name\"},\"required\":true,\"maxLength\":50},{\"key\":\"currency\",\"type\":\"select\",\"label\":{\"en-US\":\"Currency\"},\"required\":true,\"options\":[{\"value\":\"TRY\",\"label\":\"Turkish Lira (₺)\"},{\"value\":\"USD\",\"label\":\"US Dollar ($)\"},{\"value\":\"EUR\",\"label\":\"Euro (€)\"}]},{\"key\":\"branchCode\",\"type\":\"select\",\"label\":{\"en-US\":\"Branch\"},\"required\":true,\"options\":[{\"value\":\"001\",\"label\":\"Main Branch - Istanbul\"},{\"value\":\"002\",\"label\":\"Ankara Branch\"},{\"value\":\"003\",\"label\":\"Izmir Branch\"}]}]},{\"key\":\"initial-deposit\",\"title\":{\"en-US\":\"Initial Deposit\"},\"fields\":[{\"key\":\"initialDeposit\",\"type\":\"number\",\"label\":{\"en-US\":\"Initial Deposit Amount\"},\"placeholder\":{\"en-US\":\"0.00\"},\"required\":false,\"min\":0,\"step\":0.01,\"currency\":true},{\"key\":\"accountPurpose\",\"type\":\"select\",\"label\":{\"en-US\":\"Account Purpose\"},\"required\":true,\"options\":[{\"value\":\"personal\",\"label\":{\"en-US\":\"Personal Use\"}},{\"value\":\"business\",\"label\":{\"en-US\":\"Business Use\"}},{\"value\":\"savings\",\"label\":{\"en-US\":\"Savings\"}}]}]},{\"key\":\"notifications\",\"title\":{\"en-US\":\"Notification Preferences\"},\"fields\":[{\"key\":\"smsNotifications\",\"type\":\"checkbox\",\"label\":{\"en-US\":\"SMS Notifications\"},\"description\":{\"en-US\":\"Receive transaction alerts via SMS\"},\"defaultValue\":true},{\"key\":\"emailNotifications\",\"type\":\"checkbox\",\"label\":{\"en-US\":\"Email Notifications\"},\"description\":{\"en-US\":\"Receive account statements via email\"},\"defaultValue\":true},{\"key\":\"pushNotifications\",\"type\":\"checkbox\",\"label\":{\"en-US\":\"Push Notifications\"},\"description\":{\"en-US\":\"Receive real-time notifications on your mobile device\"},\"defaultValue\":true}]}],\"buttons\":[{\"key\":\"submit-account-details\",\"label\":{\"en-US\":\"Continue\"},\"style\":\"primary\",\"action\":\"submit\",\"transition\":\"submit-account-details\",\"requiresValidForm\":true},{\"key\":\"back\",\"label\":{\"en-US\":\"Back\"},\"style\":\"secondary\",\"action\":\"back\"}]}" } } diff --git a/core/Views/account-opening/account-details-submit-confirmation-view.json b/core/Views/account-opening/account-details-submit-confirmation-view.json index 404b384..404a482 100644 --- a/core/Views/account-opening/account-details-submit-confirmation-view.json +++ b/core/Views/account-opening/account-details-submit-confirmation-view.json @@ -17,13 +17,9 @@ { "language": "en-US", "label": "Confirm Account Details" - }, - { - "language": "tr-TR", - "label": "Hesap Bilgilerini Onayla" } ], "display": "popup", - "content": "{\"type\":\"confirmation-dialog\",\"title\":{\"en-US\":\"Confirm Account Details\",\"tr-TR\":\"Hesap Bilgilerini Onayla\"},\"message\":{\"en-US\":\"Please review your account details before proceeding. Once submitted, some information cannot be changed.\",\"tr-TR\":\"Devam etmeden önce hesap bilgilerinizi gözden geçirin. Gönderildikten sonra bazı bilgiler değiştirilemez.\"},\"icon\":\"warning\",\"showAccountSummary\":true,\"buttons\":[{\"key\":\"confirm\",\"label\":{\"en-US\":\"Submit Details\",\"tr-TR\":\"Bilgileri Gönder\"},\"style\":\"primary\",\"action\":\"submit\"},{\"key\":\"edit\",\"label\":{\"en-US\":\"Edit Details\",\"tr-TR\":\"Bilgileri Düzenle\"},\"style\":\"secondary\",\"action\":\"cancel\"}]}" + "content": "{\"type\":\"confirmation-dialog\",\"title\":{\"en-US\":\"Confirm Account Details\"},\"message\":{\"en-US\":\"Please review your account details before proceeding. Once submitted, some information cannot be changed.\"},\"icon\":\"warning\",\"showAccountSummary\":true,\"buttons\":[{\"key\":\"confirm\",\"label\":{\"en-US\":\"Submit Details\"},\"style\":\"primary\",\"action\":\"submit\"},{\"key\":\"edit\",\"label\":{\"en-US\":\"Edit Details\"},\"style\":\"secondary\",\"action\":\"cancel\"}]}" } } diff --git a/core/Views/account-opening/account-opening-success-view.json b/core/Views/account-opening/account-opening-success-view.json index e4aec06..43e8c6f 100644 --- a/core/Views/account-opening/account-opening-success-view.json +++ b/core/Views/account-opening/account-opening-success-view.json @@ -16,13 +16,9 @@ { "language": "en-US", "label": "Account Opening Successful" - }, - { - "language": "tr-TR", - "label": "Hesap Açılışı Başarılı" } ], "display": "bottom-sheet", - "content": "{\"type\":\"success-celebration\",\"animation\":\"confetti\",\"title\":{\"en-US\":\"🎉 Congratulations!\",\"tr-TR\":\"🎉 Tebrikler!\"},\"subtitle\":{\"en-US\":\"Your account has been successfully created\",\"tr-TR\":\"Hesabınız başarıyla oluşturuldu\"},\"message\":{\"en-US\":\"Welcome to our digital banking family! Your new account is ready to use.\",\"tr-TR\":\"Dijital bankacılık ailemize hoş geldiniz! Yeni hesabınız kullanıma hazır.\"},\"accountSummary\":{\"title\":{\"en-US\":\"Account Details\",\"tr-TR\":\"Hesap Detayları\"},\"fields\":[{\"key\":\"accountNumber\",\"label\":{\"en-US\":\"Account Number\",\"tr-TR\":\"Hesap Numarası\"},\"value\":\"{{accountCreation.accountNumber}}\",\"copyable\":true,\"highlight\":true},{\"key\":\"iban\",\"label\":{\"en-US\":\"IBAN\",\"tr-TR\":\"IBAN\"},\"value\":\"{{accountCreation.iban}}\",\"copyable\":true},{\"key\":\"accountType\",\"label\":{\"en-US\":\"Account Type\",\"tr-TR\":\"Hesap Türü\"},\"value\":\"{{accountCreation.accountType}}\",\"displayValue\":{\"demand-deposit\":{\"en-US\":\"Demand Deposit Account\",\"tr-TR\":\"Vadesiz Mevduat Hesabı\"}}},{\"key\":\"currency\",\"label\":{\"en-US\":\"Currency\",\"tr-TR\":\"Para Birimi\"},\"value\":\"{{accountCreation.currency}}\"},{\"key\":\"status\",\"label\":{\"en-US\":\"Status\",\"tr-TR\":\"Durum\"},\"value\":\"{{accountCreation.status}}\",\"displayValue\":{\"active\":{\"en-US\":\"Active ✅\",\"tr-TR\":\"Aktif ✅\"}},\"statusColor\":\"success\"}]},\"nextSteps\":{\"title\":{\"en-US\":\"What's Next?\",\"tr-TR\":\"Sırada Ne Var?\"},\"items\":[{\"icon\":\"credit-card\",\"title\":{\"en-US\":\"Order Your Debit Card\",\"tr-TR\":\"Banka Kartınızı Sipariş Edin\"},\"description\":{\"en-US\":\"Get instant access to your funds\",\"tr-TR\":\"Paranıza anında erişim sağlayın\"},\"action\":\"order-card\"},{\"icon\":\"mobile-app\",\"title\":{\"en-US\":\"Download Mobile App\",\"tr-TR\":\"Mobil Uygulamayı İndirin\"},\"description\":{\"en-US\":\"Manage your account on the go\",\"tr-TR\":\"Hesabınızı her yerden yönetin\"},\"action\":\"download-app\"},{\"icon\":\"money-transfer\",\"title\":{\"en-US\":\"Make Your First Deposit\",\"tr-TR\":\"İlk Yatırımınızı Yapın\"},\"description\":{\"en-US\":\"Start using your new account\",\"tr-TR\":\"Yeni hesabınızı kullanmaya başlayın\"},\"action\":\"make-deposit\"}]},\"buttons\":[{\"key\":\"continue-banking\",\"label\":{\"en-US\":\"Continue to Banking\",\"tr-TR\":\"Bankacılığa Devam Et\"},\"style\":\"primary\",\"action\":\"navigate\",\"destination\":\"/dashboard\",\"icon\":\"arrow-right\"},{\"key\":\"download-summary\",\"label\":{\"en-US\":\"Download Summary\",\"tr-TR\":\"Özet İndir\"},\"style\":\"secondary\",\"action\":\"download\",\"fileType\":\"pdf\",\"icon\":\"download\"},{\"key\":\"share-success\",\"label\":{\"en-US\":\"Share\",\"tr-TR\":\"Paylaş\"},\"style\":\"tertiary\",\"action\":\"share\",\"icon\":\"share\"}],\"footer\":{\"message\":{\"en-US\":\"Need help? Contact our 24/7 customer support\",\"tr-TR\":\"Yardıma mı ihtiyacınız var? 7/24 müşteri desteğimizle iletişime geçin\"},\"supportAction\":{\"label\":{\"en-US\":\"Get Support\",\"tr-TR\":\"Destek Al\"},\"action\":\"contact-support\"}}}" + "content": "{\"type\":\"success-celebration\",\"animation\":\"confetti\",\"title\":{\"en-US\":\"🎉 Congratulations!\"},\"subtitle\":{\"en-US\":\"Your account has been successfully created\"},\"message\":{\"en-US\":\"Welcome to our digital banking family! Your new account is ready to use.\"},\"accountSummary\":{\"title\":{\"en-US\":\"Account Details\"},\"fields\":[{\"key\":\"accountNumber\",\"label\":{\"en-US\":\"Account Number\"},\"value\":\"{{accountCreation.accountNumber}}\",\"copyable\":true,\"highlight\":true},{\"key\":\"iban\",\"label\":{\"en-US\":\"IBAN\"},\"value\":\"{{accountCreation.iban}}\",\"copyable\":true},{\"key\":\"accountType\",\"label\":{\"en-US\":\"Account Type\"},\"value\":\"{{accountCreation.accountType}}\",\"displayValue\":{\"demand-deposit\":{\"en-US\":\"Demand Deposit Account\"}}},{\"key\":\"currency\",\"label\":{\"en-US\":\"Currency\"},\"value\":\"{{accountCreation.currency}}\"},{\"key\":\"status\",\"label\":{\"en-US\":\"Status\"},\"value\":\"{{accountCreation.status}}\",\"displayValue\":{\"active\":{\"en-US\":\"Active ✅\"}},\"statusColor\":\"success\"}]},\"nextSteps\":{\"title\":{\"en-US\":\"What's Next?\"},\"items\":[{\"icon\":\"credit-card\",\"title\":{\"en-US\":\"Order Your Debit Card\"},\"description\":{\"en-US\":\"Get instant access to your funds\"},\"action\":\"order-card\"},{\"icon\":\"mobile-app\",\"title\":{\"en-US\":\"Download Mobile App\"},\"description\":{\"en-US\":\"Manage your account on the go\"},\"action\":\"download-app\"},{\"icon\":\"money-transfer\",\"title\":{\"en-US\":\"Make Your First Deposit\"},\"description\":{\"en-US\":\"Start using your new account\"},\"action\":\"make-deposit\"}]},\"buttons\":[{\"key\":\"continue-banking\",\"label\":{\"en-US\":\"Continue to Banking\"},\"style\":\"primary\",\"action\":\"navigate\",\"destination\":\"/dashboard\",\"icon\":\"arrow-right\"},{\"key\":\"download-summary\",\"label\":{\"en-US\":\"Download Summary\"},\"style\":\"secondary\",\"action\":\"download\",\"fileType\":\"pdf\",\"icon\":\"download\"},{\"key\":\"share-success\",\"label\":{\"en-US\":\"Share\"},\"style\":\"tertiary\",\"action\":\"share\",\"icon\":\"share\"}],\"footer\":{\"message\":{\"en-US\":\"Need help? Contact our 24/7 customer support\"},\"supportAction\":{\"label\":{\"en-US\":\"Get Support\"},\"action\":\"contact-support\"}}}" } } diff --git a/core/Views/account-opening/account-type-selection-confirmation-view.json b/core/Views/account-opening/account-type-selection-confirmation-view.json index e2a63a7..eb8f704 100644 --- a/core/Views/account-opening/account-type-selection-confirmation-view.json +++ b/core/Views/account-opening/account-type-selection-confirmation-view.json @@ -17,13 +17,9 @@ { "language": "en-US", "label": "Confirm Account Type Selection" - }, - { - "language": "tr-TR", - "label": "Hesap Türü Seçimini Onayla" } ], "display": "popup", - "content": "{\"type\":\"confirmation-dialog\",\"title\":{\"en-US\":\"Confirm Account Type\",\"tr-TR\":\"Hesap Türünü Onayla\"},\"message\":{\"en-US\":\"You have selected a Demand Deposit Account. This account type allows you to deposit and withdraw money at any time. Do you want to proceed?\",\"tr-TR\":\"Vadesiz Mevduat Hesabı seçtiniz. Bu hesap türü istediğiniz zaman para yatırıp çekmenize olanak sağlar. Devam etmek istiyor musunuz?\"},\"icon\":\"info\",\"buttons\":[{\"key\":\"confirm\",\"label\":{\"en-US\":\"Yes, Continue\",\"tr-TR\":\"Evet, Devam Et\"},\"style\":\"primary\",\"action\":\"submit\"},{\"key\":\"cancel\",\"label\":{\"en-US\":\"Go Back\",\"tr-TR\":\"Geri Dön\"},\"style\":\"secondary\",\"action\":\"cancel\"}]}" + "content": "{\"type\":\"confirmation-dialog\",\"title\":{\"en-US\":\"Confirm Account Type\"},\"message\":{\"en-US\":\"You have selected a Demand Deposit Account. This account type allows you to deposit and withdraw money at any time. Do you want to proceed?\"},\"icon\":\"info\",\"buttons\":[{\"key\":\"confirm\",\"label\":{\"en-US\":\"Yes, Continue\"},\"style\":\"primary\",\"action\":\"submit\"},{\"key\":\"cancel\",\"label\":{\"en-US\":\"Go Back\"},\"style\":\"secondary\",\"action\":\"cancel\"}]}" } } diff --git a/core/Views/account-opening/account-type-selection-view.json b/core/Views/account-opening/account-type-selection-view.json index ad374cf..920e42c 100644 --- a/core/Views/account-opening/account-type-selection-view.json +++ b/core/Views/account-opening/account-type-selection-view.json @@ -16,60 +16,48 @@ { "language": "en-US", "label": "Select Account Type" - }, - { - "language": "tr-TR", - "label": "Hesap Türü Seçimi" } ], "display": "full-page", "content": { "type": "form", "title": { - "en-US": "Choose Your Account Type", - "tr-TR": "Hesap Türünüzü Seçin" + "en-US": "Choose Your Account Type" }, "description": { - "en-US": "Select the type of account that best suits your needs.", - "tr-TR": "İhtiyaçlarınıza en uygun hesap türünü seçin." + "en-US": "Select the type of account that best suits your needs." }, "fields": [ { "key": "accountType", "type": "radio-group", "label": { - "en-US": "Account Type", - "tr-TR": "Hesap Türü" + "en-US": "Account Type" }, "required": true, "options": [ { "value": "demand-deposit", "label": { - "en-US": "Demand Deposit Account", - "tr-TR": "Vadesiz Mevduat Hesabı" + "en-US": "Demand Deposit Account" }, "description": { - "en-US": "Access your money anytime with no minimum balance requirement", - "tr-TR": "Minimum bakiye şartı olmadan paranıza istediğiniz zaman erişin" + "en-US": "Access your money anytime with no minimum balance requirement" }, "icon": "credit-card" }, { "value": "savings", "label": { - "en-US": "Savings Account", - "tr-TR": "Tasarruf Hesabı" + "en-US": "Savings Account" }, "description": { - "en-US": "Earn interest on your deposits with competitive rates", - "tr-TR": "Rekabetçi oranlarla mevduatınızdan faiz kazanın" + "en-US": "Earn interest on your deposits with competitive rates" }, "icon": "piggy-bank", "disabled": true, "disabledReason": { - "en-US": "Coming soon", - "tr-TR": "Yakında" + "en-US": "Coming soon" } } ] @@ -79,8 +67,7 @@ { "key": "select-demand-deposit", "label": { - "en-US": "Continue", - "tr-TR": "Devam Et" + "en-US": "Continue" }, "style": "primary", "action": "submit", @@ -90,4 +77,4 @@ ] } } -} \ No newline at end of file +} diff --git a/core/Views/account-opening/final-confirmation-popup-view.json b/core/Views/account-opening/final-confirmation-popup-view.json index 3f5e79c..20a1d69 100644 --- a/core/Views/account-opening/final-confirmation-popup-view.json +++ b/core/Views/account-opening/final-confirmation-popup-view.json @@ -17,13 +17,9 @@ { "language": "en-US", "label": "Final Confirmation" - }, - { - "language": "tr-TR", - "label": "Son Onay" } ], "display": "popup", - "content": "{\"type\":\"terms-confirmation-dialog\",\"title\":{\"en-US\":\"Final Account Opening Confirmation\",\"tr-TR\":\"Son Hesap Açılış Onayı\"},\"message\":{\"en-US\":\"By proceeding, you confirm that all information provided is accurate and you agree to our terms and conditions.\",\"tr-TR\":\"Devam ederek, verilen tüm bilgilerin doğru olduğunu onaylıyor ve şartlar ve koşullarımızı kabul ettiğinizi beyan ediyorsunuz.\"},\"icon\":\"document-check\",\"showFullAccountSummary\":true,\"termsAndConditions\":{\"required\":true,\"links\":[{\"key\":\"terms\",\"label\":{\"en-US\":\"Terms and Conditions\",\"tr-TR\":\"Şartlar ve Koşullar\"},\"url\":\"/terms-and-conditions\"},{\"key\":\"privacy\",\"label\":{\"en-US\":\"Privacy Policy\",\"tr-TR\":\"Gizlilik Politikası\"},\"url\":\"/privacy-policy\"}]},\"checkboxes\":[{\"key\":\"terms-accepted\",\"label\":{\"en-US\":\"I accept the Terms and Conditions\",\"tr-TR\":\"Şartlar ve Koşulları kabul ediyorum\"},\"required\":true},{\"key\":\"privacy-accepted\",\"label\":{\"en-US\":\"I accept the Privacy Policy\",\"tr-TR\":\"Gizlilik Politikasını kabul ediyorum\"},\"required\":true},{\"key\":\"marketing-consent\",\"label\":{\"en-US\":\"I consent to receive marketing communications (optional)\",\"tr-TR\":\"Pazarlama iletişimi almayı kabul ediyorum (isteğe bağlı)\"},\"required\":false}],\"buttons\":[{\"key\":\"confirm\",\"label\":{\"en-US\":\"Open Account\",\"tr-TR\":\"Hesap Aç\"},\"style\":\"primary\",\"action\":\"submit\",\"requiresAllMandatoryCheckboxes\":true},{\"key\":\"cancel\",\"label\":{\"en-US\":\"Cancel\",\"tr-TR\":\"İptal\"},\"style\":\"secondary\",\"action\":\"cancel\"}]}" + "content": "{\"type\":\"terms-confirmation-dialog\",\"title\":{\"en-US\":\"Final Account Opening Confirmation\"},\"message\":{\"en-US\":\"By proceeding, you confirm that all information provided is accurate and you agree to our terms and conditions.\"},\"icon\":\"document-check\",\"showFullAccountSummary\":true,\"termsAndConditions\":{\"required\":true,\"links\":[{\"key\":\"terms\",\"label\":{\"en-US\":\"Terms and Conditions\"},\"url\":\"/terms-and-conditions\"},{\"key\":\"privacy\",\"label\":{\"en-US\":\"Privacy Policy\"},\"url\":\"/privacy-policy\"}]},\"checkboxes\":[{\"key\":\"terms-accepted\",\"label\":{\"en-US\":\"I accept the Terms and Conditions\"},\"required\":true},{\"key\":\"privacy-accepted\",\"label\":{\"en-US\":\"I accept the Privacy Policy\"},\"required\":true},{\"key\":\"marketing-consent\",\"label\":{\"en-US\":\"I consent to receive marketing communications (optional)\"},\"required\":false}],\"buttons\":[{\"key\":\"confirm\",\"label\":{\"en-US\":\"Open Account\"},\"style\":\"primary\",\"action\":\"submit\",\"requiresAllMandatoryCheckboxes\":true},{\"key\":\"cancel\",\"label\":{\"en-US\":\"Cancel\"},\"style\":\"secondary\",\"action\":\"cancel\"}]}" } } diff --git a/core/Views/account-opening/policy-validation-view.json b/core/Views/account-opening/policy-validation-view.json index 4c2cd38..557c1ff 100644 --- a/core/Views/account-opening/policy-validation-view.json +++ b/core/Views/account-opening/policy-validation-view.json @@ -16,13 +16,9 @@ { "language": "en-US", "label": "Policy Validation" - }, - { - "language": "tr-TR", - "label": "Politika Doğrulama" } ], "display": "full-page", - "content": "{\"type\":\"loading-screen\",\"title\":{\"en-US\":\"Validating Your Information\",\"tr-TR\":\"Bilgileriniz Doğrulanıyor\"},\"description\":{\"en-US\":\"We are checking your account opening request against our policies. This may take a few moments.\",\"tr-TR\":\"Hesap açılış talebinizi politikalarımıza göre kontrol ediyoruz. Bu birkaç dakika sürebilir.\"},\"loadingIndicator\":true,\"showProgress\":true,\"steps\":[{\"key\":\"compliance-check\",\"label\":{\"en-US\":\"Compliance Check\",\"tr-TR\":\"Uyumluluk Kontrolü\"}},{\"key\":\"risk-assessment\",\"label\":{\"en-US\":\"Risk Assessment\",\"tr-TR\":\"Risk Değerlendirmesi\"}},{\"key\":\"policy-validation\",\"label\":{\"en-US\":\"Policy Validation\",\"tr-TR\":\"Politika Doğrulama\"}}]}" + "content": "{\"type\":\"loading-screen\",\"title\":{\"en-US\":\"Validating Your Information\"},\"description\":{\"en-US\":\"We are checking your account opening request against our policies. This may take a few moments.\"},\"loadingIndicator\":true,\"showProgress\":true,\"steps\":[{\"key\":\"compliance-check\",\"label\":{\"en-US\":\"Compliance Check\"}},{\"key\":\"risk-assessment\",\"label\":{\"en-US\":\"Risk Assessment\"}},{\"key\":\"policy-validation\",\"label\":{\"en-US\":\"Policy Validation\"}}]}" } } diff --git a/core/Views/subflow-test/child-workflow-view.json b/core/Views/subflow-test/child-workflow-view.json index d7518b4..5068929 100644 --- a/core/Views/subflow-test/child-workflow-view.json +++ b/core/Views/subflow-test/child-workflow-view.json @@ -16,14 +16,9 @@ { "language": "en-US", "label": "Child Workflow View" - }, - { - "language": "tr-TR", - "label": "Alt Akış Görünümü" } ], "display": "full-page", - "content": "{\"type\":\"form\",\"title\":{\"en-US\":\"Child Workflow - Active\",\"tr-TR\":\"Alt Akış - Aktif\"},\"description\":{\"en-US\":\"This is the child (subflow) workflow view. Complete this workflow to continue the parent.\",\"tr-TR\":\"Bu alt akış görünümüdür. Ana akışın devam etmesi için bu akışı tamamlayın.\"},\"fields\":[{\"key\":\"childStatus\",\"type\":\"text\",\"label\":{\"en-US\":\"Child Status\",\"tr-TR\":\"Alt Akış Durumu\"},\"readonly\":true,\"defaultValue\":\"active\"},{\"key\":\"childInfo\",\"type\":\"info\",\"label\":{\"en-US\":\"Child Info\",\"tr-TR\":\"Alt Akış Bilgisi\"},\"content\":{\"en-US\":\"This view includes child extension data for longpolling test.\",\"tr-TR\":\"Bu görünüm longpolling testi için alt akış extension verisini içerir.\"}}],\"buttons\":[{\"key\":\"complete\",\"label\":{\"en-US\":\"Complete Child Workflow\",\"tr-TR\":\"Alt Akışı Tamamla\"},\"style\":\"success\",\"action\":\"submit\",\"transition\":\"complete-child\"}]}" + "content": "{\"type\":\"form\",\"title\":{\"en-US\":\"Child Workflow - Active\"},\"description\":{\"en-US\":\"This is the child (subflow) workflow view. Complete this workflow to continue the parent.\"},\"fields\":[{\"key\":\"childStatus\",\"type\":\"text\",\"label\":{\"en-US\":\"Child Status\"},\"readonly\":true,\"defaultValue\":\"active\"},{\"key\":\"childInfo\",\"type\":\"info\",\"label\":{\"en-US\":\"Child Info\"},\"content\":{\"en-US\":\"This view includes child extension data for longpolling test.\"}}],\"buttons\":[{\"key\":\"complete\",\"label\":{\"en-US\":\"Complete Child Workflow\"},\"style\":\"success\",\"action\":\"submit\",\"transition\":\"complete-child\"}]}" } } - diff --git a/core/Views/subflow-test/grandchild-workflow-view.json b/core/Views/subflow-test/grandchild-workflow-view.json index 62bd4d3..68624e9 100644 --- a/core/Views/subflow-test/grandchild-workflow-view.json +++ b/core/Views/subflow-test/grandchild-workflow-view.json @@ -17,13 +17,9 @@ { "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\"}]}" + "content": "{\"type\":\"form\",\"title\":{\"en-US\":\"Grandchild Workflow - Active (Level 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.\"},\"fields\":[{\"key\":\"grandchildStatus\",\"type\":\"text\",\"label\":{\"en-US\":\"Grandchild Status\"},\"readonly\":true,\"defaultValue\":\"active\"},{\"key\":\"nestingLevel\",\"type\":\"text\",\"label\":{\"en-US\":\"Nesting Level\"},\"readonly\":true,\"defaultValue\":\"3 (Parent > Child > Grandchild)\"},{\"key\":\"grandchildInfo\",\"type\":\"info\",\"label\":{\"en-US\":\"Grandchild Info\"},\"content\":{\"en-US\":\"This view includes grandchild extension data for deep nested longpolling test.\"}}],\"buttons\":[{\"key\":\"complete\",\"label\":{\"en-US\":\"Complete Grandchild Workflow\"},\"style\":\"success\",\"action\":\"submit\",\"transition\":\"complete-grandchild\"}]}" } } diff --git a/core/Views/subflow-test/parent-workflow-view.json b/core/Views/subflow-test/parent-workflow-view.json index 86b39a4..48a3c54 100644 --- a/core/Views/subflow-test/parent-workflow-view.json +++ b/core/Views/subflow-test/parent-workflow-view.json @@ -16,14 +16,9 @@ { "language": "en-US", "label": "Parent Workflow View" - }, - { - "language": "tr-TR", - "label": "Ana Akış Görünümü" } ], "display": "full-page", - "content": "{\"type\":\"form\",\"title\":{\"en-US\":\"Parent Workflow - Waiting for SubFlow\",\"tr-TR\":\"Ana Akış - Alt Akış Bekleniyor\"},\"description\":{\"en-US\":\"This is the parent workflow view. The workflow is waiting for the subflow to complete.\",\"tr-TR\":\"Bu ana akış görünümüdür. Akış, alt akışın tamamlanmasını beklemektedir.\"},\"fields\":[{\"key\":\"status\",\"type\":\"text\",\"label\":{\"en-US\":\"Current Status\",\"tr-TR\":\"Mevcut Durum\"},\"readonly\":true,\"defaultValue\":\"waiting-for-subflow\"},{\"key\":\"parentInfo\",\"type\":\"info\",\"label\":{\"en-US\":\"Parent Info\",\"tr-TR\":\"Üst Akış Bilgisi\"},\"content\":{\"en-US\":\"This view includes parent extension data for longpolling test.\",\"tr-TR\":\"Bu görünüm longpolling testi için üst akış extension verisini içerir.\"}}],\"buttons\":[{\"key\":\"proceed\",\"label\":{\"en-US\":\"Proceed After SubFlow\",\"tr-TR\":\"Alt Akıştan Sonra Devam Et\"},\"style\":\"primary\",\"action\":\"submit\",\"transition\":\"proceed-after-subflow\"}]}" + "content": "{\"type\":\"form\",\"title\":{\"en-US\":\"Parent Workflow - Waiting for SubFlow\"},\"description\":{\"en-US\":\"This is the parent workflow view. The workflow is waiting for the subflow to complete.\"},\"fields\":[{\"key\":\"status\",\"type\":\"text\",\"label\":{\"en-US\":\"Current Status\"},\"readonly\":true,\"defaultValue\":\"waiting-for-subflow\"},{\"key\":\"parentInfo\",\"type\":\"info\",\"label\":{\"en-US\":\"Parent Info\"},\"content\":{\"en-US\":\"This view includes parent extension data for longpolling test.\"}}],\"buttons\":[{\"key\":\"proceed\",\"label\":{\"en-US\":\"Proceed After SubFlow\"},\"style\":\"primary\",\"action\":\"submit\",\"transition\":\"proceed-after-subflow\"}]}" } } - diff --git a/core/Workflows/account-opening/account-opening-workflow.json b/core/Workflows/account-opening/account-opening-workflow.json index 9fe4f48..ac49431 100644 --- a/core/Workflows/account-opening/account-opening-workflow.json +++ b/core/Workflows/account-opening/account-opening-workflow.json @@ -26,10 +26,6 @@ { "language": "en-US", "label": "Digital Banking Account Opening Workflow" - }, - { - "language": "tr-TR", - "label": "Dijital Bankacılık Hesap Açılışı İş Akışı" } ], "functions": [ @@ -78,10 +74,6 @@ { "language": "en-US", "label": "Initiate Account Opening" - }, - { - "language": "tr-TR", - "label": "Hesap Açılışını Başlat" } ], "schema": { @@ -120,10 +112,6 @@ { "language": "en-US", "label": "Account Type Selection" - }, - { - "language": "tr-TR", - "label": "Hesap Türü Seçimi" } ], "view": { @@ -161,10 +149,6 @@ { "language": "en-US", "label": "Select Demand Deposit Account" - }, - { - "language": "tr-TR", - "label": "Vadesiz Hesap Seç" } ], "schema": { @@ -236,10 +220,6 @@ { "language": "en-US", "label": "Execute SubProcess" - }, - { - "language": "tr-TR", - "label": "Sub Process Çalıştır" } ], "schema": null, @@ -266,7 +246,7 @@ }, "mapping": { "location": "./src/TriggerScheduledPaymentsMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CgpwdWJsaWMgY2xhc3MgVHJpZ2dlclNjaGVkdWxlZFBheW1lbnRzTWFwcGluZyA6U2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBzdWJQcm9jZXNzVGFzayA9IHRhc2sgYXMgU3ViUHJvY2Vzc1Rhc2s7CgogICAgICAgIGlmIChzdWJQcm9jZXNzVGFzayA9PSBudWxsKQogICAgICAgIHsKICAgICAgICAgICAgdGhyb3cgbmV3IEludmFsaWRPcGVyYXRpb25FeGNlcHRpb24oIlRhc2sgbXVzdCBiZSBhIFN1YlByb2Nlc3NUYXNrIik7CiAgICAgICAgfQoKICAgICAgICAvLyBDb25maWd1cmUgc3VicHJvY2VzcwogICAgICAgIHN1YlByb2Nlc3NUYXNrLlNldERvbWFpbigiY29yZSIpOwogICAgICAgIHN1YlByb2Nlc3NUYXNrLlNldEtleSgic2NoZWR1bGVkLXBheW1lbnRzIik7CiAgICAgICAgc3ViUHJvY2Vzc1Rhc2suU2V0VmVyc2lvbigiMS4wLjAiKTsKCiAgICAgICAgLy8gUHJlcGFyZSBzdWJwcm9jZXNzIGluaXRpYWxpemF0aW9uIGRhdGEKICAgICAgICAvLyBQYXNzIHJlbGV2YW50IGRhdGEgZnJvbSBhY2NvdW50LW9wZW5pbmcgd29ya2Zsb3cgdG8gc2NoZWR1bGVkLXBheW1lbnRzCiAgICAgICAgc3ViUHJvY2Vzc1Rhc2suU2V0Qm9keShuZXcKICAgICAgICB7CiAgICAgICAgICAgIHVzZXJJZCA9IDEsCiAgICAgICAgICAgIGFtb3VudCA9IDEyMDAwLAogICAgICAgICAgICBjdXJyZW5jeSA9ICJUTCIsCiAgICAgICAgICAgIGZyZXF1ZW5jeSA9ICJtb250aGx5IiwKICAgICAgICAgICAgc3RhcnREYXRlID0gRGF0ZVRpbWUuVXRjTm93LAogICAgICAgICAgICBlbmREYXRlID0gIjIwMjYtMTAtMDFUMDk6MDI6MzguMjAxWiIsCiAgICAgICAgICAgIHBheW1lbnRNZXRob2RJZCA9ICIxIiwKICAgICAgICAgICAgZGVzY3JpcHRpb24gPSAiWCDDtmRlbWUiLAogICAgICAgICAgICByZWNpcGllbnRJZCA9ICIzMjQzMjQiLAogICAgICAgICAgICBpc0F1dG9SZXRyeSA9IGZhbHNlLAogICAgICAgICAgICBtYXhSZXRyaWVzID0gMwogICAgICAgIH0pOwoKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgIHsKICAgICAgICAgICAgRGF0YSA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGEKICAgICAgICB9KTsKICAgIH0KCiAgICBwdWJsaWMgYXN5bmMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIHJlc3BvbnNlID0gbmV3IFNjcmlwdFJlc3BvbnNlKCk7CgogICAgICAgIC8vIFN1YlByb2Nlc3MgaXMgZmlyZS1hbmQtZm9yZ2V0CiAgICAgICAgLy8gSnVzdCB0cmFjayB0aGF0IGl0IHdhcyBpbml0aWF0ZWQKICAgICAgCiAgICAgICAgICAgIHJlc3BvbnNlLkRhdGEgPSBuZXcKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgc2NoZWR1bGVkUGF5bWVudHNJbml0aWF0ZWQgPSB0cnVlLAogICAgICAgICAgICAgICAgaW5pdGlhdGVkQXQgPSBEYXRlVGltZS5VdGNOb3csCiAgICAgICAgICAgICAgICBzdGF0dXMgPSAiU0NIRURVTEVEX1BBWU1FTlRTX1NVQlBST0NFU1NfTEFVTkNIRUQiCiAgICAgICAgICAgIH07CgogICAgICAgIHJldHVybiByZXNwb25zZTsKICAgIH0KfQoK" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CgpwdWJsaWMgY2xhc3MgVHJpZ2dlclNjaGVkdWxlZFBheW1lbnRzTWFwcGluZyA6U2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBzdWJQcm9jZXNzVGFzayA9IHRhc2sgYXMgU3ViUHJvY2Vzc1Rhc2s7CgogICAgICAgIGlmIChzdWJQcm9jZXNzVGFzayA9PSBudWxsKQogICAgICAgIHsKICAgICAgICAgICAgdGhyb3cgbmV3IEludmFsaWRPcGVyYXRpb25FeGNlcHRpb24oIlRhc2sgbXVzdCBiZSBhIFN1YlByb2Nlc3NUYXNrIik7CiAgICAgICAgfQoKICAgICAgICAvLyBDb25maWd1cmUgc3VicHJvY2VzcwogICAgICAgIHN1YlByb2Nlc3NUYXNrLlNldERvbWFpbigiY29yZSIpOwogICAgICAgIHN1YlByb2Nlc3NUYXNrLlNldEtleSgic2NoZWR1bGVkLXBheW1lbnRzIik7CiAgICAgICAgc3ViUHJvY2Vzc1Rhc2suU2V0VmVyc2lvbigiMS4wLjAiKTsKCiAgICAgICAgLy8gUHJlcGFyZSBzdWJwcm9jZXNzIGluaXRpYWxpemF0aW9uIGRhdGEKICAgICAgICAvLyBQYXNzIHJlbGV2YW50IGRhdGEgZnJvbSBhY2NvdW50LW9wZW5pbmcgd29ya2Zsb3cgdG8gc2NoZWR1bGVkLXBheW1lbnRzCiAgICAgICAgc3ViUHJvY2Vzc1Rhc2suU2V0Qm9keShuZXcKICAgICAgICB7CiAgICAgICAgICAgIHVzZXJJZCA9IDEsCiAgICAgICAgICAgIGFtb3VudCA9IDEyMDAwLAogICAgICAgICAgICBjdXJyZW5jeSA9ICJUTCIsCiAgICAgICAgICAgIGZyZXF1ZW5jeSA9ICJtb250aGx5IiwKICAgICAgICAgICAgc3RhcnREYXRlID0gRGF0ZVRpbWUuVXRjTm93LAogICAgICAgICAgICBlbmREYXRlID0gIjIwMjYtMTAtMDFUMDk6MDI6MzguMjAxWiIsCiAgICAgICAgICAgIHBheW1lbnRNZXRob2RJZCA9ICIxIiwKICAgICAgICAgICAgZGVzY3JpcHRpb24gPSAiU2NoZWR1bGVkIHBheW1lbnQgYm9vdHN0cmFwIiwKICAgICAgICAgICAgcmVjaXBpZW50SWQgPSAiMzI0MzI0IiwKICAgICAgICAgICAgaXNBdXRvUmV0cnkgPSBmYWxzZSwKICAgICAgICAgICAgbWF4UmV0cmllcyA9IDMKICAgICAgICB9KTsKCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICB7CiAgICAgICAgICAgIERhdGEgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhCiAgICAgICAgfSk7CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciByZXNwb25zZSA9IG5ldyBTY3JpcHRSZXNwb25zZSgpOwoKICAgICAgICAvLyBTdWJQcm9jZXNzIGlzIGZpcmUtYW5kLWZvcmdldAogICAgICAgIC8vIEp1c3QgdHJhY2sgdGhhdCBpdCB3YXMgaW5pdGlhdGVkCiAgICAgIAogICAgICAgICAgICByZXNwb25zZS5EYXRhID0gbmV3CiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIHNjaGVkdWxlZFBheW1lbnRzSW5pdGlhdGVkID0gdHJ1ZSwKICAgICAgICAgICAgICAgIGluaXRpYXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LAogICAgICAgICAgICAgICAgc3RhdHVzID0gIlNDSEVEVUxFRF9QQVlNRU5UU19TVUJQUk9DRVNTX0xBVU5DSEVEIgogICAgICAgICAgICB9OwoKICAgICAgICByZXR1cm4gcmVzcG9uc2U7CiAgICB9Cn0KCg==" } } ] @@ -282,10 +262,6 @@ { "language": "en-US", "label": "Account Details Input" - }, - { - "language": "tr-TR", - "label": "Hesap Bilgileri Girişi" } ], "view": { @@ -331,10 +307,6 @@ { "language": "en-US", "label": "Submit Account Details" - }, - { - "language": "tr-TR", - "label": "Hesap Bilgilerini Gönder" } ], "schema": { @@ -368,10 +340,6 @@ { "language": "en-US", "label": "Account Confirmation" - }, - { - "language": "tr-TR", - "label": "Hesap Onayı" } ], "queryRoles": [ @@ -417,10 +385,6 @@ { "language": "en-US", "label": "Confirm Account Opening" - }, - { - "language": "tr-TR", - "label": "Hesap Açılışını Onayla" } ], "schema": { @@ -454,10 +418,6 @@ { "language": "en-US", "label": "Policy Validation" - }, - { - "language": "tr-TR", - "label": "Politika Doğrulama" } ], "view": { @@ -497,10 +457,6 @@ { "language": "en-US", "label": "Policies Passed" - }, - { - "language": "tr-TR", - "label": "Politikalar Geçti" } ], "schema": null, @@ -521,10 +477,6 @@ { "language": "en-US", "label": "Policies Failed - Retry" - }, - { - "language": "tr-TR", - "label": "Politikalar Başarısız - Tekrar Dene" } ], "schema": null, @@ -547,10 +499,6 @@ { "language": "en-US", "label": "Account Creation" - }, - { - "language": "tr-TR", - "label": "Hesap Oluşturma" } ], "view": { @@ -590,10 +538,6 @@ { "language": "en-US", "label": "Account Created Successfully" - }, - { - "language": "tr-TR", - "label": "Hesap Başarıyla Oluşturuldu" } ], "schema": null, @@ -614,10 +558,6 @@ { "language": "en-US", "label": "Account Creation Failed - Retry" - }, - { - "language": "tr-TR", - "label": "Hesap Oluşturma Başarısız - Tekrar Dene" } ], "schema": null, @@ -640,10 +580,6 @@ { "language": "en-US", "label": "Account Opening Successful" - }, - { - "language": "tr-TR", - "label": "Hesap Açılışı Başarılı" } ], "view": { @@ -669,10 +605,6 @@ { "language": "en-US", "label": "Account Opening Cancelled" - }, - { - "language": "tr-TR", - "label": "Hesap Açılışı İptal Edildi" } ], "view": null, @@ -689,10 +621,6 @@ { "language": "en-US", "label": "Account Opening Timeout" - }, - { - "language": "tr-TR", - "label": "Hesap Açılışı Zaman Aşımına Uğradı" } ], "view": null, diff --git a/core/Workflows/account-opening/src/TriggerScheduledPaymentsMapping.csx b/core/Workflows/account-opening/src/TriggerScheduledPaymentsMapping.csx index 2701c89..1bf452d 100644 --- a/core/Workflows/account-opening/src/TriggerScheduledPaymentsMapping.csx +++ b/core/Workflows/account-opening/src/TriggerScheduledPaymentsMapping.csx @@ -30,7 +30,7 @@ public class TriggerScheduledPaymentsMapping :ScriptBase, IMapping startDate = DateTime.UtcNow, endDate = "2026-10-01T09:02:38.201Z", paymentMethodId = "1", - description = "X ödeme", + description = "Scheduled payment bootstrap", recipientId = "324324", isAutoRetry = false, maxRetries = 3 diff --git a/core/Workflows/collection-object-test/collection-object-test-workflow.json b/core/Workflows/collection-object-test/collection-object-test-workflow.json index 405909a..34d756f 100644 --- a/core/Workflows/collection-object-test/collection-object-test-workflow.json +++ b/core/Workflows/collection-object-test/collection-object-test-workflow.json @@ -33,10 +33,6 @@ { "language": "en-US", "label": "Dynamic Collection & Object Test Workflow" - }, - { - "language": "tr-TR", - "label": "Dinamik Koleksiyon ve Nesne Test İş Akışı" } ], "functions": [], @@ -52,10 +48,6 @@ { "language": "en-US", "label": "Start Collection & Object Test" - }, - { - "language": "tr-TR", - "label": "Koleksiyon ve Nesne Testini Başlat" } ], "onExecutionTasks": [ @@ -84,10 +76,6 @@ { "language": "en-US", "label": "Test CreateObject, CreateList, SetProperty, ListAdd" - }, - { - "language": "tr-TR", - "label": "CreateObject, CreateList, SetProperty, ListAdd Testi" } ], "view": null, @@ -118,10 +106,6 @@ { "language": "en-US", "label": "CreateObject / SetProperty Completed" - }, - { - "language": "tr-TR", - "label": "CreateObject / SetProperty Tamamlandı" } ], "schema": null, @@ -144,10 +128,6 @@ { "language": "en-US", "label": "Test GetList, AsList" - }, - { - "language": "tr-TR", - "label": "GetList, AsList Testi" } ], "view": null, @@ -178,10 +158,6 @@ { "language": "en-US", "label": "GetList / AsList Completed" - }, - { - "language": "tr-TR", - "label": "GetList / AsList Tamamlandı" } ], "schema": null, @@ -204,10 +180,6 @@ { "language": "en-US", "label": "Test ListFilter, ListCount, ListAny" - }, - { - "language": "tr-TR", - "label": "ListFilter, ListCount, ListAny Testi" } ], "view": null, @@ -238,10 +210,6 @@ { "language": "en-US", "label": "ListFilter / ListCount / ListAny Completed" - }, - { - "language": "tr-TR", - "label": "ListFilter / ListCount / ListAny Tamamlandı" } ], "schema": null, @@ -264,10 +232,6 @@ { "language": "en-US", "label": "Test ListFirst, ListLast" - }, - { - "language": "tr-TR", - "label": "ListFirst, ListLast Testi" } ], "view": null, @@ -298,10 +262,6 @@ { "language": "en-US", "label": "ListFirst / ListLast Completed" - }, - { - "language": "tr-TR", - "label": "ListFirst / ListLast Tamamlandı" } ], "schema": null, @@ -324,10 +284,6 @@ { "language": "en-US", "label": "Test ListSelect" - }, - { - "language": "tr-TR", - "label": "ListSelect Testi" } ], "view": null, @@ -358,10 +314,6 @@ { "language": "en-US", "label": "ListSelect Completed" - }, - { - "language": "tr-TR", - "label": "ListSelect Tamamlandı" } ], "schema": null, @@ -384,10 +336,6 @@ { "language": "en-US", "label": "Test ListAdd, ListRemove" - }, - { - "language": "tr-TR", - "label": "ListAdd, ListRemove Testi" } ], "view": null, @@ -418,10 +366,6 @@ { "language": "en-US", "label": "ListAdd / ListRemove Completed" - }, - { - "language": "tr-TR", - "label": "ListAdd / ListRemove Tamamlandı" } ], "schema": null, @@ -444,10 +388,6 @@ { "language": "en-US", "label": "Test RemoveProperty, ToDictionary, HasProperty" - }, - { - "language": "tr-TR", - "label": "RemoveProperty, ToDictionary, HasProperty Testi" } ], "view": null, @@ -478,10 +418,6 @@ { "language": "en-US", "label": "RemoveProperty / ToDictionary Completed" - }, - { - "language": "tr-TR", - "label": "RemoveProperty / ToDictionary Tamamlandı" } ], "schema": null, @@ -504,10 +440,6 @@ { "language": "en-US", "label": "All Tests Completed" - }, - { - "language": "tr-TR", - "label": "Tüm Testler Tamamlandı" } ], "view": null, @@ -518,4 +450,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/error-boundary/error-boundary-test-workflow.json b/core/Workflows/error-boundary/error-boundary-test-workflow.json index 488872d..ceedebe 100644 --- a/core/Workflows/error-boundary/error-boundary-test-workflow.json +++ b/core/Workflows/error-boundary/error-boundary-test-workflow.json @@ -31,10 +31,6 @@ { "language": "en-US", "label": "Error Boundary Integration Test" - }, - { - "language": "tr-TR", - "label": "Hata Sinir Entegrasyon Testi" } ], "functions": [], @@ -61,10 +57,6 @@ { "language": "en-US", "label": "Start Error Boundary Test" - }, - { - "language": "tr-TR", - "label": "Hata Sinir Testini Baslat" } ], "onExecutionTasks": [ @@ -93,10 +85,6 @@ { "language": "en-US", "label": "Retry Test State" - }, - { - "language": "tr-TR", - "label": "Yeniden Deneme Test Durumu" } ], "view": null, @@ -140,10 +128,6 @@ { "language": "en-US", "label": "Auto to Ignore Test" - }, - { - "language": "tr-TR", - "label": "Yoksayma Testine Otomatik Gecis" } ], "rule": { @@ -163,10 +147,6 @@ { "language": "en-US", "label": "Ignore Test State" - }, - { - "language": "tr-TR", - "label": "Yoksayma Test Durumu" } ], "view": null, @@ -224,10 +204,6 @@ { "language": "en-US", "label": "Auto to Rollback from Ignore" - }, - { - "language": "tr-TR", - "label": "Ignore'dan Rollback'e Otomatik" } ], "rule": { @@ -247,10 +223,6 @@ { "language": "en-US", "label": "Rollback Action Test" - }, - { - "language": "tr-TR", - "label": "Rollback Action Testi" } ], "view": null, @@ -303,10 +275,6 @@ { "language": "en-US", "label": "Auto to Log from Rollback" - }, - { - "language": "tr-TR", - "label": "Rollback'den Log'a Otomatik" } ], "rule": { @@ -326,10 +294,6 @@ { "language": "en-US", "label": "Log Action Test" - }, - { - "language": "tr-TR", - "label": "Log Action Testi" } ], "view": null, @@ -381,10 +345,6 @@ { "language": "en-US", "label": "Auto to Completed from Log" - }, - { - "language": "tr-TR", - "label": "Log'dan Completed'a Otomatik" } ], "rule": { @@ -404,10 +364,6 @@ { "language": "en-US", "label": "Notify Action Test" - }, - { - "language": "tr-TR", - "label": "Notify Action Testi" } ], "view": null, @@ -460,10 +416,6 @@ { "language": "en-US", "label": "Auto to Completed from Notify" - }, - { - "language": "tr-TR", - "label": "Notify'dan Completed'a Otomatik" } ], "rule": { @@ -483,10 +435,6 @@ { "language": "en-US", "label": "Success Final" - }, - { - "language": "tr-TR", - "label": "Basari Son" } ], "view": null, @@ -504,10 +452,6 @@ { "language": "en-US", "label": "Error Final" - }, - { - "language": "tr-TR", - "label": "Hata Son" } ], "view": null, @@ -518,4 +462,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/flow-types/core-flow-test.json b/core/Workflows/flow-types/core-flow-test.json index 879d9e8..ebe8f94 100644 --- a/core/Workflows/flow-types/core-flow-test.json +++ b/core/Workflows/flow-types/core-flow-test.json @@ -17,10 +17,6 @@ { "language": "en-US", "label": "Core Flow Type Integration Test" - }, - { - "language": "tr-TR", - "label": "Cekirdek Akis Tipi Entegrasyon Testi" } ], "functions": [], @@ -36,10 +32,6 @@ { "language": "en-US", "label": "Start Core Flow Test" - }, - { - "language": "tr-TR", - "label": "Cekirdek Akis Testini Baslat" } ], "onExecutionTasks": [ @@ -68,10 +60,6 @@ { "language": "en-US", "label": "Core Init State" - }, - { - "language": "tr-TR", - "label": "Cekirdek Baslangic Durumu" } ], "view": null, @@ -102,10 +90,6 @@ { "language": "en-US", "label": "Auto to Core Completed" - }, - { - "language": "tr-TR", - "label": "Cekirdek Tamamlanmaya Otomatik Gecis" } ], "rule": { @@ -125,10 +109,6 @@ { "language": "en-US", "label": "Core Completed (Final)" - }, - { - "language": "tr-TR", - "label": "Cekirdek Tamamlandi (Final)" } ], "view": null, @@ -139,4 +119,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/flow-types/subprocess-flow-test.json b/core/Workflows/flow-types/subprocess-flow-test.json index 0302194..ab1e063 100644 --- a/core/Workflows/flow-types/subprocess-flow-test.json +++ b/core/Workflows/flow-types/subprocess-flow-test.json @@ -17,10 +17,6 @@ { "language": "en-US", "label": "SubProcess Flow Type Integration Test" - }, - { - "language": "tr-TR", - "label": "Alt Surec Akis Tipi Entegrasyon Testi" } ], "functions": [], @@ -36,10 +32,6 @@ { "language": "en-US", "label": "Start SubProcess Flow Test" - }, - { - "language": "tr-TR", - "label": "Alt Surec Akis Testini Baslat" } ], "onExecutionTasks": [ @@ -68,10 +60,6 @@ { "language": "en-US", "label": "SubProcess Init State" - }, - { - "language": "tr-TR", - "label": "Alt Surec Baslangic Durumu" } ], "view": null, @@ -102,10 +90,6 @@ { "language": "en-US", "label": "Auto to SubProcess Completed" - }, - { - "language": "tr-TR", - "label": "Alt Surec Tamamlanmaya Otomatik Gecis" } ], "rule": { @@ -125,10 +109,6 @@ { "language": "en-US", "label": "SubProcess Completed (Final)" - }, - { - "language": "tr-TR", - "label": "Alt Surec Tamamlandi (Final)" } ], "view": null, @@ -139,4 +119,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/instance-management/.meta/instance-management-test-workflow.diagram.json b/core/Workflows/instance-management/.meta/instance-management-test-workflow.diagram.json index 00a4467..4f2b4f9 100644 --- a/core/Workflows/instance-management/.meta/instance-management-test-workflow.diagram.json +++ b/core/Workflows/instance-management/.meta/instance-management-test-workflow.diagram.json @@ -1,3 +1,40 @@ { - "nodePos": {} + "nodePos": { + "__start__": { + "x": 390, + "y": 30 + }, + "active-state": { + "x": 300, + "y": 220 + }, + "processing-state": { + "x": 600, + "y": 440 + }, + "completed-state": { + "x": 0, + "y": 660 + }, + "rejected-state": { + "x": 300, + "y": 660 + }, + "timeout-state": { + "x": 590, + "y": 150 + }, + "suspended-state": { + "x": 600, + "y": 660 + }, + "busy-state": { + "x": 900, + "y": 660 + }, + "human-state": { + "x": 1200, + "y": 660 + } + } } \ No newline at end of file diff --git a/core/Workflows/instance-management/instance-management-test-workflow.json b/core/Workflows/instance-management/instance-management-test-workflow.json index 034ae12..4977b18 100644 --- a/core/Workflows/instance-management/instance-management-test-workflow.json +++ b/core/Workflows/instance-management/instance-management-test-workflow.json @@ -32,10 +32,6 @@ { "language": "en-US", "label": "Instance Management Integration Test" - }, - { - "language": "tr-TR", - "label": "Ornek Yonetimi Entegrasyon Testi" } ], "functions": [], @@ -51,10 +47,6 @@ { "language": "en-US", "label": "Start Instance Management Test" - }, - { - "language": "tr-TR", - "label": "Ornek Yonetimi Testini Baslat" } ], "onExecutionTasks": [ @@ -83,10 +75,6 @@ { "language": "en-US", "label": "Active State" - }, - { - "language": "tr-TR", - "label": "Aktif Durum" } ], "view": null, @@ -103,10 +91,6 @@ { "language": "en-US", "label": "Process" - }, - { - "language": "tr-TR", - "label": "Isle" } ], "schema": null, @@ -123,10 +107,6 @@ { "language": "en-US", "label": "Fast Complete" - }, - { - "language": "tr-TR", - "label": "Hizli Tamamla" } ], "schema": null, @@ -145,10 +125,6 @@ { "language": "en-US", "label": "Processing State" - }, - { - "language": "tr-TR", - "label": "Isleniyor Durumu" } ], "view": null, @@ -165,10 +141,6 @@ { "language": "en-US", "label": "Finish" - }, - { - "language": "tr-TR", - "label": "Bitir" } ], "schema": null, @@ -185,10 +157,6 @@ { "language": "en-US", "label": "Reject" - }, - { - "language": "tr-TR", - "label": "Reddet" } ], "schema": null, @@ -205,10 +173,6 @@ { "language": "en-US", "label": "Suspend" - }, - { - "language": "tr-TR", - "label": "Askiya Al" } ], "schema": null, @@ -225,10 +189,6 @@ { "language": "en-US", "label": "Set Busy" - }, - { - "language": "tr-TR", - "label": "Mesgul Yap" } ], "schema": null, @@ -245,10 +205,6 @@ { "language": "en-US", "label": "Assign Human" - }, - { - "language": "tr-TR", - "label": "Insan Ata" } ], "schema": null, @@ -267,10 +223,6 @@ { "language": "en-US", "label": "Completed State" - }, - { - "language": "tr-TR", - "label": "Tamamlandi Durumu" } ], "view": null, @@ -288,10 +240,6 @@ { "language": "en-US", "label": "Rejected State" - }, - { - "language": "tr-TR", - "label": "Reddedildi Durumu" } ], "view": null, @@ -309,10 +257,6 @@ { "language": "en-US", "label": "Timeout State" - }, - { - "language": "tr-TR", - "label": "Zaman Asimi Durumu" } ], "view": null, @@ -330,10 +274,6 @@ { "language": "en-US", "label": "Suspended State" - }, - { - "language": "tr-TR", - "label": "Askiya Alinmis Durumu" } ], "view": null, @@ -351,10 +291,6 @@ { "language": "en-US", "label": "Busy State" - }, - { - "language": "tr-TR", - "label": "Mesgul Durumu" } ], "view": null, @@ -372,10 +308,6 @@ { "language": "en-US", "label": "Human State" - }, - { - "language": "tr-TR", - "label": "Insan Mudahalesi Durumu" } ], "view": null, @@ -386,4 +318,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json index 5e00605..b8bfa54 100644 --- a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json +++ b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json @@ -35,10 +35,6 @@ { "language": "en-US", "label": "Lifecycle & Transitions Integration Test" - }, - { - "language": "tr-TR", - "label": "Yasam Dongusu ve Gecis Entegrasyon Testi" } ], "functions": [], @@ -62,10 +58,6 @@ { "language": "en-US", "label": "Cancel Workflow" - }, - { - "language": "tr-TR", - "label": "Is Akisini Iptal Et" } ] }, @@ -83,10 +75,6 @@ { "language": "en-US", "label": "Exit Workflow" - }, - { - "language": "tr-TR", - "label": "Is Akisindan Cik" } ], "onExecutionTasks": [ @@ -114,10 +102,6 @@ { "language": "en-US", "label": "Start Lifecycle Test" - }, - { - "language": "tr-TR", - "label": "Yasam Dongusu Testini Baslat" } ], "onExecutionTasks": [ @@ -146,10 +130,6 @@ { "language": "en-US", "label": "Initialize State (Initial)" - }, - { - "language": "tr-TR", - "label": "Baslangic Durumu" } ], "view": null, @@ -166,10 +146,6 @@ { "language": "en-US", "label": "Move to Processing (Manual + ITransitionMapping)" - }, - { - "language": "tr-TR", - "label": "Isleme Gec (Manuel + ITransitionMapping)" } ], "roles": [ @@ -197,10 +173,6 @@ { "language": "en-US", "label": "Processing State (Intermediate, onEntries + onExits)" - }, - { - "language": "tr-TR", - "label": "Isleme Durumu (Ara Durum, onEntries + onExits)" } ], "view": null, @@ -245,10 +217,6 @@ { "language": "en-US", "label": "Auto Pass (testPath=pass)" - }, - { - "language": "tr-TR", - "label": "Otomatik Gecis (testPath=pass)" } ], "rule": { @@ -266,10 +234,6 @@ { "language": "en-US", "label": "Auto Fail (testPath=fail)" - }, - { - "language": "tr-TR", - "label": "Otomatik Basarisiz (testPath=fail)" } ], "rule": { @@ -288,10 +252,6 @@ { "language": "en-US", "label": "Default Auto (DefaultAutoFallbackRule + triggerKind:10)" - }, - { - "language": "tr-TR", - "label": "Varsayilan Otomatik (DefaultAutoFallbackRule + triggerKind:10)" } ], "rule": { @@ -311,10 +271,6 @@ { "language": "en-US", "label": "Auto Passed State (Intermediate)" - }, - { - "language": "tr-TR", - "label": "Otomatik Gecis Basarili Durumu" } ], "view": null, @@ -331,10 +287,6 @@ { "language": "en-US", "label": "Scheduled Timer (ITimerMapping, 6s)" - }, - { - "language": "tr-TR", - "label": "Zamanlanmis Gecis (ITimerMapping, 6s)" } ], "timer": { @@ -352,10 +304,6 @@ { "language": "en-US", "label": "Cancel Schedule Manually" - }, - { - "language": "tr-TR", - "label": "Schedule Manuel Iptal" } ] }, @@ -368,10 +316,6 @@ { "language": "en-US", "label": "Reschedule Timer" - }, - { - "language": "tr-TR", - "label": "Timer Yeniden Zamanla" } ] } @@ -386,10 +330,6 @@ { "language": "en-US", "label": "Timer Triggered State" - }, - { - "language": "tr-TR", - "label": "Zamanlayici Tetiklenmis Durum" } ], "view": null, @@ -420,10 +360,6 @@ { "language": "en-US", "label": "Auto to Pre-Complete (AlwaysTrue)" - }, - { - "language": "tr-TR", - "label": "Otomatik On-Tamamlama Gecisi" } ], "rule": { @@ -443,10 +379,6 @@ { "language": "en-US", "label": "Pre-Complete State (Manual to Finish)" - }, - { - "language": "tr-TR", - "label": "Tamamlama Oncesi Durum" } ], "queryRoles": [ @@ -473,10 +405,6 @@ { "language": "en-US", "label": "Complete Workflow (Manual to Final)" - }, - { - "language": "tr-TR", - "label": "Is Akisini Tamamla" } ], "schema": null, @@ -494,10 +422,6 @@ { "language": "en-US", "label": "Default Fallback State (triggerKind:10 target)" - }, - { - "language": "tr-TR", - "label": "Varsayilan Geri Donus Durumu" } ], "view": null, @@ -514,10 +438,6 @@ { "language": "en-US", "label": "Fallback Auto Complete" - }, - { - "language": "tr-TR", - "label": "Geri Donus Otomatik Tamamlama" } ], "rule": { @@ -537,10 +457,6 @@ { "language": "en-US", "label": "Auto Failed State (Final/Error)" - }, - { - "language": "tr-TR", - "label": "Otomatik Basarisiz Durumu (Final/Hata)" } ], "view": null, @@ -558,10 +474,6 @@ { "language": "en-US", "label": "Completed State (Final/Success)" - }, - { - "language": "tr-TR", - "label": "Tamamlandi Durumu (Final/Basarili)" } ], "view": null, @@ -579,10 +491,6 @@ { "language": "en-US", "label": "Terminated State (Final/Terminated)" - }, - { - "language": "tr-TR", - "label": "Sonlandirildi Durumu (Final/Sonlandirildi)" } ], "view": null, @@ -593,4 +501,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/payments/payment-notification-subflow.json b/core/Workflows/payments/payment-notification-subflow.json index 921c06e..9e97d6f 100644 --- a/core/Workflows/payments/payment-notification-subflow.json +++ b/core/Workflows/payments/payment-notification-subflow.json @@ -18,10 +18,6 @@ { "language": "en-US", "label": "Payment Notification Subflow" - }, - { - "language": "tr-TR", - "label": "Ödeme Bildirim Alt Akışı" } ], "functions": [], @@ -37,10 +33,6 @@ { "language": "en-US", "label": "Start Notification Process" - }, - { - "language": "tr-TR", - "label": "Bildirim Sürecini Başlat" } ], "onExecutionTasks": [] @@ -55,10 +47,6 @@ { "language": "en-US", "label": "Get User Information" - }, - { - "language": "tr-TR", - "label": "Kullanıcı Bilgilerini Al" } ], "view": null, @@ -89,10 +77,6 @@ { "language": "en-US", "label": "Auto - User Info Retrieved" - }, - { - "language": "tr-TR", - "label": "Otomatik - Kullanıcı Bilgisi Alındı" } ], "schema": null, @@ -115,10 +99,6 @@ { "language": "en-US", "label": "Send Notifications (SMS + Push)" - }, - { - "language": "tr-TR", - "label": "Bildirimler Gönder (SMS + Push)" } ], "view": null, @@ -134,7 +114,7 @@ }, "mapping": { "location": "./src/SendPaymentNotificationSmsMapping.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gU2VuZCBQYXltZW50IE5vdGlmaWNhdGlvbiBTTVMgTWFwcGluZyAtIFByZXBhcmVzIFNNUyBub3RpZmljYXRpb24gZGF0YQovLy8gPC9zdW1tYXJ5PgpwdWJsaWMgY2xhc3MgU2VuZFBheW1lbnROb3RpZmljYXRpb25TbXNNYXBwaW5nIDogSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgdmFyIGh0dHBUYXNrID0gdGFzayBhcyBIdHRwVGFzazsKICAgICAgICAgICAgaWYgKGh0dHBUYXNrID09IG51bGwpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIHRocm93IG5ldyBJbnZhbGlkT3BlcmF0aW9uRXhjZXB0aW9uKCJUYXNrIG11c3QgYmUgYW4gSHR0cFRhc2sgZm9yIFNNUyBub3RpZmljYXRpb24iKTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy8gR2V0IHBheW1lbnQgcmVzdWx0IGZyb20gY29udGV4dAogICAgICAgICAgICB2YXIgcGF5bWVudFJlc3VsdCA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGE/LnBheW1lbnRSZXN1bHQ7CiAgICAgICAgICAgIHZhciBwYXltZW50U2NoZWR1bGUgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhPy5wYXltZW50U2NoZWR1bGU7CiAgICAgICAgICAgIHZhciB1c2VySWQgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhPy51c2VySWQ7CgogICAgICAgICAgICAvLyBEZXRlcm1pbmUgbWVzc2FnZSBiYXNlZCBvbiBwYXltZW50IHN0YXR1cwogICAgICAgICAgICB2YXIgbWVzc2FnZSA9ICIiOwogICAgICAgICAgICB2YXIgbGFuZ3VhZ2UgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhPy5sYW5ndWFnZSA/PyAidHItVFIiOwoKICAgICAgICAgICAgaWYgKHBheW1lbnRSZXN1bHQ/LnN1Y2Nlc3MgPT0gdHJ1ZSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgaWYgKGxhbmd1YWdlID09ICJlbi1VUyIpCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9ICQiUGF5bWVudCBzdWNjZXNzZnVsISBBbW91bnQ6IHtwYXltZW50UmVzdWx0Py5hbW91bnR9IHtwYXltZW50UmVzdWx0Py5jdXJyZW5jeX0uIFJlZmVyZW5jZToge3BheW1lbnRSZXN1bHQ/LnRyYW5zYWN0aW9uSWR9IjsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGVsc2UKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gJCLDlmRlbWUgYmHFn2FyxLFsxLEhIE1pa3Rhcjoge3BheW1lbnRSZXN1bHQ/LmFtb3VudH0ge3BheW1lbnRSZXN1bHQ/LmN1cnJlbmN5fS4gUmVmZXJhbnM6IHtwYXltZW50UmVzdWx0Py50cmFuc2FjdGlvbklkfSI7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgZWxzZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBpZiAobGFuZ3VhZ2UgPT0gImVuLVVTIikKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gJCJQYXltZW50IGZhaWxlZC4gUmVhc29uOiB7cGF5bWVudFJlc3VsdD8uZXJyb3JNZXNzYWdlID8/ICJVbmtub3duIGVycm9yIn0uIFBsZWFzZSBjaGVjayB5b3VyIHBheW1lbnQgZGV0YWlscy4iOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgZWxzZQogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSAkIsOWZGVtZSBiYcWfYXLEsXPEsXouIE5lZGVuOiB7cGF5bWVudFJlc3VsdD8uZXJyb3JNZXNzYWdlID8/ICJCaWxpbm1leWVuIHNlYmVwIn0uIEzDvHRmZW4gw7ZkZW1lIGJpbGdpbGVyaW5pemkga29udHJvbCBlZGluLiI7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIC8vIFByZXBhcmUgU01TIGRhdGEKICAgICAgICAgICAgdmFyIHNtc0RhdGEgPSBuZXcKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgdG8gPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhPy5waG9uZU51bWJlciA/PyAiW1Bob25lIE5vdCBGb3VuZF0iLCAvLyBTaG91bGQgY29tZSBmcm9tIHVzZXIgZGF0YQogICAgICAgICAgICAgICAgbWVzc2FnZSA9IG1lc3NhZ2UsCiAgICAgICAgICAgICAgICB1c2VySWQgPSB1c2VySWQsCiAgICAgICAgICAgICAgICBzbXNUeXBlID0gInBheW1lbnQtbm90aWZpY2F0aW9uIiwKICAgICAgICAgICAgICAgIHRlbXBsYXRlSWQgPSAicGF5bWVudC1ub3RpZi10ZW1wbGF0ZSIsCiAgICAgICAgICAgICAgICBsYW5ndWFnZSA9IGxhbmd1YWdlLAogICAgICAgICAgICAgICAgc2VuZGVySWQgPSAiUGF5bWVudFN5c3RlbSIsCiAgICAgICAgICAgICAgICB0aW1lc3RhbXAgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoImlzbzgiKQogICAgICAgICAgICB9OwoKICAgICAgICAgICAgaHR0cFRhc2suU2V0Qm9keShzbXNEYXRhKTsKCiAgICAgICAgICAgIC8vIFNldCBIZWFkZXJzCiAgICAgICAgICAgIHZhciBoZWFkZXJzID0gbmV3IERpY3Rpb25hcnk8c3RyaW5nLCBzdHJpbmc/PgogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBbIkNvbnRlbnQtVHlwZSJdID0gImFwcGxpY2F0aW9uL2pzb24iLAogICAgICAgICAgICAgICAgWyJBY2NlcHQiXSA9ICJhcHBsaWNhdGlvbi9qc29uIiwKICAgICAgICAgICAgICAgIFsiWC1QYXltZW50LUluc3RhbmNlIl0gPSBjb250ZXh0Lkluc3RhbmNlPy5JZC5Ub1N0cmluZygpLAogICAgICAgICAgICAgICAgWyJYLVVzZXItSWQiXSA9IHVzZXJJZD8uVG9TdHJpbmcoKQogICAgICAgICAgICB9OwoKICAgICAgICAgICAgaHR0cFRhc2suU2V0SGVhZGVycyhoZWFkZXJzKTsKCiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgICAgIH0KICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGV4KQogICAgICAgIHsKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gInNtcy1ub3RpZmljYXRpb24tZXJyb3IiLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldyB7IGVycm9yID0gZXguTWVzc2FnZSB9CiAgICAgICAgICAgIH0pOwogICAgICAgIH0KICAgIH0KCiAgICBwdWJsaWMgYXN5bmMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdHJ5CiAgICAgICAgewogICAgICAgICAgICB2YXIgc3RhdHVzQ29kZSA9IGNvbnRleHQuQm9keT8uc3RhdHVzQ29kZSA/PyA1MDA7CiAgICAgICAgICAgIHZhciByZXNwb25zZURhdGEgPSBjb250ZXh0LkJvZHk/LmRhdGE7CgogICAgICAgICAgICBpZiAoc3RhdHVzQ29kZSA+PSAyMDAgJiYgc3RhdHVzQ29kZSA8IDMwMCkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIEtleSA9ICJzbXMtbm90aWZpY2F0aW9uLXN1Y2Nlc3MiLAogICAgICAgICAgICAgICAgICAgIERhdGEgPSBuZXcKICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgIHNtc1NlbnQgPSB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlSWQgPSByZXNwb25zZURhdGE/Lm1lc3NhZ2VJZCwKICAgICAgICAgICAgICAgICAgICAgICAgc2VudEF0ID0gRGF0ZVRpbWUuVXRjTm93LAogICAgICAgICAgICAgICAgICAgICAgICBzbXNTdGF0dXMgPSAic2VudCIKICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgIFRhZ3MgPSBuZXdbXSB7ICJwYXltZW50cyIsICJzbXMiLCAibm90aWZpY2F0aW9uIiwgInN1Y2Nlc3MiIH0KICAgICAgICAgICAgICAgIH07CiAgICAgICAgICAgIH0KICAgICAgICAgICAgZWxzZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgS2V5ID0gInNtcy1ub3RpZmljYXRpb24tZmFpbHVyZSIsCiAgICAgICAgICAgICAgICAgICAgRGF0YSA9IG5ldwogICAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAgICAgc21zU2VudCA9IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICBlcnJvciA9ICJGYWlsZWQgdG8gc2VuZCBTTVMgbm90aWZpY2F0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3JDb2RlID0gInNtc19zZW5kX2ZhaWxlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgIHN0YXR1c0NvZGUgPSBzdGF0dXNDb2RlLAogICAgICAgICAgICAgICAgICAgICAgICBzbXNTdGF0dXMgPSAiZmFpbGVkIgogICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgVGFncyA9IG5ld1tdIHsgInBheW1lbnRzIiwgInNtcyIsICJub3RpZmljYXRpb24iLCAiZmFpbHVyZSIgfQogICAgICAgICAgICAgICAgfTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGV4KQogICAgICAgIHsKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAic21zLW5vdGlmaWNhdGlvbi1leGNlcHRpb24iLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldwogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIHNtc1NlbnQgPSBmYWxzZSwKICAgICAgICAgICAgICAgICAgICBlcnJvciA9ICJJbnRlcm5hbCBwcm9jZXNzaW5nIGVycm9yIiwKICAgICAgICAgICAgICAgICAgICBlcnJvckNvZGUgPSAicHJvY2Vzc2luZ19lcnJvciIsCiAgICAgICAgICAgICAgICAgICAgZXJyb3JEZXNjcmlwdGlvbiA9IGV4Lk1lc3NhZ2UsCiAgICAgICAgICAgICAgICAgICAgc21zU3RhdHVzID0gImV4Y2VwdGlvbiIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICBUYWdzID0gbmV3W10geyAicGF5bWVudHMiLCAic21zIiwgIm5vdGlmaWNhdGlvbiIsICJlcnJvciIgfQogICAgICAgICAgICB9OwogICAgICAgIH0KICAgIH0KfQo=" + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gU2VuZCBQYXltZW50IE5vdGlmaWNhdGlvbiBTTVMgTWFwcGluZyAtIFByZXBhcmVzIFNNUyBub3RpZmljYXRpb24gZGF0YQovLy8gPC9zdW1tYXJ5PgpwdWJsaWMgY2xhc3MgU2VuZFBheW1lbnROb3RpZmljYXRpb25TbXNNYXBwaW5nIDogSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgdmFyIGh0dHBUYXNrID0gdGFzayBhcyBIdHRwVGFzazsKICAgICAgICAgICAgaWYgKGh0dHBUYXNrID09IG51bGwpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIHRocm93IG5ldyBJbnZhbGlkT3BlcmF0aW9uRXhjZXB0aW9uKCJUYXNrIG11c3QgYmUgYW4gSHR0cFRhc2sgZm9yIFNNUyBub3RpZmljYXRpb24iKTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy8gR2V0IHBheW1lbnQgcmVzdWx0IGZyb20gY29udGV4dAogICAgICAgICAgICB2YXIgcGF5bWVudFJlc3VsdCA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGE/LnBheW1lbnRSZXN1bHQ7CiAgICAgICAgICAgIHZhciBwYXltZW50U2NoZWR1bGUgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhPy5wYXltZW50U2NoZWR1bGU7CiAgICAgICAgICAgIHZhciB1c2VySWQgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhPy51c2VySWQ7CgogICAgICAgICAgICAvLyBEZXRlcm1pbmUgbWVzc2FnZSBiYXNlZCBvbiBwYXltZW50IHN0YXR1cwogICAgICAgICAgICB2YXIgbWVzc2FnZSA9ICIiOwogICAgICAgICAgICB2YXIgbGFuZ3VhZ2UgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhPy5sYW5ndWFnZSA/PyAiZW4tVVMiOwoKICAgICAgICAgICAgaWYgKHBheW1lbnRSZXN1bHQ/LnN1Y2Nlc3MgPT0gdHJ1ZSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgaWYgKGxhbmd1YWdlID09ICJlbi1VUyIpCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9ICQiUGF5bWVudCBzdWNjZXNzZnVsISBBbW91bnQ6IHtwYXltZW50UmVzdWx0Py5hbW91bnR9IHtwYXltZW50UmVzdWx0Py5jdXJyZW5jeX0uIFJlZmVyZW5jZToge3BheW1lbnRSZXN1bHQ/LnRyYW5zYWN0aW9uSWR9IjsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGVsc2UKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gJCJQYXltZW50IHN1Y2Nlc3NmdWwhIEFtb3VudDoge3BheW1lbnRSZXN1bHQ/LmFtb3VudH0ge3BheW1lbnRSZXN1bHQ/LmN1cnJlbmN5fS4gUmVmZXJlbmNlOiB7cGF5bWVudFJlc3VsdD8udHJhbnNhY3Rpb25JZH0iOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgICAgIGVsc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgaWYgKGxhbmd1YWdlID09ICJlbi1VUyIpCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9ICQiUGF5bWVudCBmYWlsZWQuIFJlYXNvbjoge3BheW1lbnRSZXN1bHQ/LmVycm9yTWVzc2FnZSA/PyAiVW5rbm93biBlcnJvciJ9LiBQbGVhc2UgY2hlY2sgeW91ciBwYXltZW50IGRldGFpbHMuIjsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGVsc2UKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gJCJQYXltZW50IGZhaWxlZC4gUmVhc29uOiB7cGF5bWVudFJlc3VsdD8uZXJyb3JNZXNzYWdlID8/ICJVbmtub3duIGVycm9yIn0uIFBsZWFzZSBjaGVjayB5b3VyIHBheW1lbnQgZGV0YWlscy4iOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgICAgICAvLyBQcmVwYXJlIFNNUyBkYXRhCiAgICAgICAgICAgIHZhciBzbXNEYXRhID0gbmV3CiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIHRvID0gY29udGV4dC5JbnN0YW5jZT8uRGF0YT8ucGhvbmVOdW1iZXIgPz8gIltQaG9uZSBOb3QgRm91bmRdIiwgLy8gU2hvdWxkIGNvbWUgZnJvbSB1c2VyIGRhdGEKICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBtZXNzYWdlLAogICAgICAgICAgICAgICAgdXNlcklkID0gdXNlcklkLAogICAgICAgICAgICAgICAgc21zVHlwZSA9ICJwYXltZW50LW5vdGlmaWNhdGlvbiIsCiAgICAgICAgICAgICAgICB0ZW1wbGF0ZUlkID0gInBheW1lbnQtbm90aWYtdGVtcGxhdGUiLAogICAgICAgICAgICAgICAgbGFuZ3VhZ2UgPSBsYW5ndWFnZSwKICAgICAgICAgICAgICAgIHNlbmRlcklkID0gIlBheW1lbnRTeXN0ZW0iLAogICAgICAgICAgICAgICAgdGltZXN0YW1wID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJpc284IikKICAgICAgICAgICAgfTsKCiAgICAgICAgICAgIGh0dHBUYXNrLlNldEJvZHkoc21zRGF0YSk7CgogICAgICAgICAgICAvLyBTZXQgSGVhZGVycwogICAgICAgICAgICB2YXIgaGVhZGVycyA9IG5ldyBEaWN0aW9uYXJ5PHN0cmluZywgc3RyaW5nPz4KICAgICAgICAgICAgewogICAgICAgICAgICAgICAgWyJDb250ZW50LVR5cGUiXSA9ICJhcHBsaWNhdGlvbi9qc29uIiwKICAgICAgICAgICAgICAgIFsiQWNjZXB0Il0gPSAiYXBwbGljYXRpb24vanNvbiIsCiAgICAgICAgICAgICAgICBbIlgtUGF5bWVudC1JbnN0YW5jZSJdID0gY29udGV4dC5JbnN0YW5jZT8uSWQuVG9TdHJpbmcoKSwKICAgICAgICAgICAgICAgIFsiWC1Vc2VyLUlkIl0gPSB1c2VySWQ/LlRvU3RyaW5nKCkKICAgICAgICAgICAgfTsKCiAgICAgICAgICAgIGh0dHBUYXNrLlNldEhlYWRlcnMoaGVhZGVycyk7CgogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9ICJzbXMtbm90aWZpY2F0aW9uLWVycm9yIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcgeyBlcnJvciA9IGV4Lk1lc3NhZ2UgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgdmFyIHN0YXR1c0NvZGUgPSBjb250ZXh0LkJvZHk/LnN0YXR1c0NvZGUgPz8gNTAwOwogICAgICAgICAgICB2YXIgcmVzcG9uc2VEYXRhID0gY29udGV4dC5Cb2R5Py5kYXRhOwoKICAgICAgICAgICAgaWYgKHN0YXR1c0NvZGUgPj0gMjAwICYmIHN0YXR1c0NvZGUgPCAzMDApCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBLZXkgPSAic21zLW5vdGlmaWNhdGlvbi1zdWNjZXNzIiwKICAgICAgICAgICAgICAgICAgICBEYXRhID0gbmV3CiAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICBzbXNTZW50ID0gdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZUlkID0gcmVzcG9uc2VEYXRhPy5tZXNzYWdlSWQsCiAgICAgICAgICAgICAgICAgICAgICAgIHNlbnRBdCA9IERhdGVUaW1lLlV0Y05vdywKICAgICAgICAgICAgICAgICAgICAgICAgc21zU3RhdHVzID0gInNlbnQiCiAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICBUYWdzID0gbmV3W10geyAicGF5bWVudHMiLCAic21zIiwgIm5vdGlmaWNhdGlvbiIsICJzdWNjZXNzIiB9CiAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICB9CiAgICAgICAgICAgIGVsc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIEtleSA9ICJzbXMtbm90aWZpY2F0aW9uLWZhaWx1cmUiLAogICAgICAgICAgICAgICAgICAgIERhdGEgPSBuZXcKICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgIHNtc1NlbnQgPSBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3IgPSAiRmFpbGVkIHRvIHNlbmQgU01TIG5vdGlmaWNhdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yQ29kZSA9ICJzbXNfc2VuZF9mYWlsZWQiLAogICAgICAgICAgICAgICAgICAgICAgICBzdGF0dXNDb2RlID0gc3RhdHVzQ29kZSwKICAgICAgICAgICAgICAgICAgICAgICAgc21zU3RhdHVzID0gImZhaWxlZCIKICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgIFRhZ3MgPSBuZXdbXSB7ICJwYXltZW50cyIsICJzbXMiLCAibm90aWZpY2F0aW9uIiwgImZhaWx1cmUiIH0KICAgICAgICAgICAgICAgIH07CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gInNtcy1ub3RpZmljYXRpb24tZXhjZXB0aW9uIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBzbXNTZW50ID0gZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgZXJyb3IgPSAiSW50ZXJuYWwgcHJvY2Vzc2luZyBlcnJvciIsCiAgICAgICAgICAgICAgICAgICAgZXJyb3JDb2RlID0gInByb2Nlc3NpbmdfZXJyb3IiLAogICAgICAgICAgICAgICAgICAgIGVycm9yRGVzY3JpcHRpb24gPSBleC5NZXNzYWdlLAogICAgICAgICAgICAgICAgICAgIHNtc1N0YXR1cyA9ICJleGNlcHRpb24iCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgVGFncyA9IG5ld1tdIHsgInBheW1lbnRzIiwgInNtcyIsICJub3RpZmljYXRpb24iLCAiZXJyb3IiIH0KICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICB9Cn0K" } }, { @@ -147,7 +127,7 @@ }, "mapping": { "location": "./src/SendPaymentPushNotificationMapping.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFNlbmRQYXltZW50UHVzaE5vdGlmaWNhdGlvbk1hcHBpbmcgOiBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdHJ5CiAgICAgICAgewogICAgICAgICAgICB2YXIgaHR0cFRhc2sgPSB0YXNrIGFzIEh0dHBUYXNrOwogICAgICAgICAgICBpZiAoaHR0cFRhc2sgPT0gbnVsbCkKICAgICAgICAgICAgICAgIHRocm93IG5ldyBJbnZhbGlkT3BlcmF0aW9uRXhjZXB0aW9uKCJUYXNrIG11c3QgYmUgYW4gSHR0cFRhc2siKTsKCiAgICAgICAgICAgIC8vIENoZWNrIGlmIHVzZXIgaGFzIHJlZ2lzdGVyZWQgZGV2aWNlcwogICAgICAgICAgICB2YXIgaGFzUmVnaXN0ZXJlZERldmljZXMgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhPy5oYXNSZWdpc3RlcmVkRGV2aWNlcyA9PSB0cnVlOwogICAgICAgICAgICBpZiAoIWhhc1JlZ2lzdGVyZWREZXZpY2VzKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAvLyBTa2lwIHB1c2ggbm90aWZpY2F0aW9uIGlmIG5vIGRldmljZXMKICAgICAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgS2V5ID0gInB1c2gtc2tpcHBlZCIsCiAgICAgICAgICAgICAgICAgICAgRGF0YSA9IG5ldyB7IHB1c2hTZW50ID0gZmFsc2UsIHJlYXNvbiA9ICJObyByZWdpc3RlcmVkIGRldmljZXMiIH0KICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICB9CgogICAgICAgICAgICAvLyBHZXQgZGF0YSBmcm9tIGNvbnRleHQKICAgICAgICAgICAgdmFyIHBheW1lbnRSZXN1bHQgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhPy5wYXltZW50UmVzdWx0OwogICAgICAgICAgICB2YXIgdXNlciA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGE/LnVzZXI7CiAgICAgICAgICAgIHZhciByZWdpc3RlcmVkRGV2aWNlcyA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGE/LnJlZ2lzdGVyZWREZXZpY2VzOwogICAgICAgICAgICB2YXIgbGFuZ3VhZ2UgPSB1c2VyPy5sYW5ndWFnZSA/PyAidHItVFIiOwoKICAgICAgICAgICAgLy8gR2VuZXJhdGUgcHVzaCBtZXNzYWdlCiAgICAgICAgICAgIHZhciB0aXRsZSA9ICIiOwogICAgICAgICAgICB2YXIgYm9keSA9ICIiOwogICAgICAgICAgICBpZiAocGF5bWVudFJlc3VsdD8uc3VjY2VzcyA9PSB0cnVlKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBpZiAobGFuZ3VhZ2UgPT0gImVuLVVTIikKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICJQYXltZW50IFN1Y2Nlc3NmdWwiOwogICAgICAgICAgICAgICAgICAgIGJvZHkgPSAkIllvdXIgcGF5bWVudCBvZiB7cGF5bWVudFJlc3VsdD8uYW1vdW50fSB7cGF5bWVudFJlc3VsdD8uY3VycmVuY3l9IGhhcyBiZWVuIHByb2Nlc3NlZCBzdWNjZXNzZnVsbHkuIjsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIGVsc2UKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICLDlmRlbWUgQmHFn2FyxLFsxLEiOwogICAgICAgICAgICAgICAgICAgIGJvZHkgPSAkIntwYXltZW50UmVzdWx0Py5hbW91bnR9IHtwYXltZW50UmVzdWx0Py5jdXJyZW5jeX0gw7ZkZW1lbml6IGJhxZ9hcsSxeWxhIGnFn2xlbWUgYWzEsW5kxLEuIjsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICBlbHNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGlmIChsYW5ndWFnZSA9PSAiZW4tVVMiKQogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIlBheW1lbnQgRmFpbGVkIjsKICAgICAgICAgICAgICAgICAgICBib2R5ID0gIllvdXIgcGF5bWVudCBjb3VsZCBub3QgYmUgcHJvY2Vzc2VkLiBQbGVhc2UgdHJ5IGFnYWluLiI7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiw5ZkZW1lIEJhxZ9hcsSxc8SxeiI7CiAgICAgICAgICAgICAgICAgICAgYm9keSA9ICLDlmRlbWVuaXogZ2Vyw6dla2xlxZ90aXJpbGVtZWRpLiBMw7x0ZmVuIHRla3JhciBkZW5leWluLiI7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIHZhciBwdXNoRGF0YSA9IG5ldwogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICB0aXRsZSA9IHRpdGxlLAogICAgICAgICAgICAgICAgYm9keSA9IGJvZHksCiAgICAgICAgICAgICAgICB1c2VySWQgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhPy51c2VySWQsCiAgICAgICAgICAgICAgICBkZXZpY2VzID0gcmVnaXN0ZXJlZERldmljZXMsCiAgICAgICAgICAgICAgICBwdXNoVHlwZSA9ICJwYXltZW50LW5vdGlmaWNhdGlvbiIsCiAgICAgICAgICAgICAgICBkYXRhID0gbmV3CiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgcGF5bWVudElkID0gcGF5bWVudFJlc3VsdD8udHJhbnNhY3Rpb25JZCwKICAgICAgICAgICAgICAgICAgICBzdWNjZXNzID0gcGF5bWVudFJlc3VsdD8uc3VjY2VzcwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9OwoKICAgICAgICAgICAgaHR0cFRhc2suU2V0Qm9keShwdXNoRGF0YSk7CiAgICAgICAgICAgIGh0dHBUYXNrLlNldEhlYWRlcnMobmV3IERpY3Rpb25hcnk8c3RyaW5nLCBzdHJpbmc/PgogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBbIkNvbnRlbnQtVHlwZSJdID0gImFwcGxpY2F0aW9uL2pzb24iCiAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IEtleSA9ICJwdXNoLWVycm9yIiwgRGF0YSA9IG5ldyB7IGVycm9yID0gZXguTWVzc2FnZSB9IH0pOwogICAgICAgIH0KICAgIH0KCiAgICBwdWJsaWMgYXN5bmMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIHN0YXR1c0NvZGUgPSBjb250ZXh0LkJvZHk/LnN0YXR1c0NvZGUgPz8gNTAwOwogICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICB7CiAgICAgICAgICAgIEtleSA9IHN0YXR1c0NvZGUgPj0gMjAwICYmIHN0YXR1c0NvZGUgPCAzMDAgPyAicHVzaC1zdWNjZXNzIiA6ICJwdXNoLWZhaWx1cmUiLAogICAgICAgICAgICBEYXRhID0gbmV3IHsgcHVzaFNlbnQgPSBzdGF0dXNDb2RlID49IDIwMCAmJiBzdGF0dXNDb2RlIDwgMzAwIH0KICAgICAgICB9OwogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFNlbmRQYXltZW50UHVzaE5vdGlmaWNhdGlvbk1hcHBpbmcgOiBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdHJ5CiAgICAgICAgewogICAgICAgICAgICB2YXIgaHR0cFRhc2sgPSB0YXNrIGFzIEh0dHBUYXNrOwogICAgICAgICAgICBpZiAoaHR0cFRhc2sgPT0gbnVsbCkKICAgICAgICAgICAgICAgIHRocm93IG5ldyBJbnZhbGlkT3BlcmF0aW9uRXhjZXB0aW9uKCJUYXNrIG11c3QgYmUgYW4gSHR0cFRhc2siKTsKCiAgICAgICAgICAgIC8vIENoZWNrIGlmIHVzZXIgaGFzIHJlZ2lzdGVyZWQgZGV2aWNlcwogICAgICAgICAgICB2YXIgaGFzUmVnaXN0ZXJlZERldmljZXMgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhPy5oYXNSZWdpc3RlcmVkRGV2aWNlcyA9PSB0cnVlOwogICAgICAgICAgICBpZiAoIWhhc1JlZ2lzdGVyZWREZXZpY2VzKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAvLyBTa2lwIHB1c2ggbm90aWZpY2F0aW9uIGlmIG5vIGRldmljZXMKICAgICAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgS2V5ID0gInB1c2gtc2tpcHBlZCIsCiAgICAgICAgICAgICAgICAgICAgRGF0YSA9IG5ldyB7IHB1c2hTZW50ID0gZmFsc2UsIHJlYXNvbiA9ICJObyByZWdpc3RlcmVkIGRldmljZXMiIH0KICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICB9CgogICAgICAgICAgICAvLyBHZXQgZGF0YSBmcm9tIGNvbnRleHQKICAgICAgICAgICAgdmFyIHBheW1lbnRSZXN1bHQgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhPy5wYXltZW50UmVzdWx0OwogICAgICAgICAgICB2YXIgdXNlciA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGE/LnVzZXI7CiAgICAgICAgICAgIHZhciByZWdpc3RlcmVkRGV2aWNlcyA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGE/LnJlZ2lzdGVyZWREZXZpY2VzOwogICAgICAgICAgICB2YXIgbGFuZ3VhZ2UgPSB1c2VyPy5sYW5ndWFnZSA/PyAiZW4tVVMiOwoKICAgICAgICAgICAgLy8gR2VuZXJhdGUgcHVzaCBtZXNzYWdlIChFbmdsaXNoIGJhc2VsaW5lOyBsb2NhbGl6ZSB2aWEgZXhwbGljaXQgbGFuZ3VhZ2UgdGFnIGlmIG5lZWRlZCBsYXRlcikKICAgICAgICAgICAgdmFyIHRpdGxlID0gIiI7CiAgICAgICAgICAgIHZhciBib2R5ID0gIiI7CiAgICAgICAgICAgIGlmIChwYXltZW50UmVzdWx0Py5zdWNjZXNzID09IHRydWUpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGlmIChsYW5ndWFnZSA9PSAiZW4tVVMiKQogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIlBheW1lbnQgU3VjY2Vzc2Z1bCI7CiAgICAgICAgICAgICAgICAgICAgYm9keSA9ICQiWW91ciBwYXltZW50IG9mIHtwYXltZW50UmVzdWx0Py5hbW91bnR9IHtwYXltZW50UmVzdWx0Py5jdXJyZW5jeX0gaGFzIGJlZW4gcHJvY2Vzc2VkIHN1Y2Nlc3NmdWxseS4iOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICBlbHNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIHRpdGxlID0gIlBheW1lbnQgU3VjY2Vzc2Z1bCI7CiAgICAgICAgICAgICAgICBib2R5ID0gJCJZb3VyIHBheW1lbnQgb2Yge3BheW1lbnRSZXN1bHQ/LmFtb3VudH0ge3BheW1lbnRSZXN1bHQ/LmN1cnJlbmN5fSBoYXMgYmVlbiBwcm9jZXNzZWQgc3VjY2Vzc2Z1bGx5LiI7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgICBlbHNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGlmIChsYW5ndWFnZSA9PSAiZW4tVVMiKQogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIlBheW1lbnQgRmFpbGVkIjsKICAgICAgICAgICAgICAgICAgICBib2R5ID0gIllvdXIgcGF5bWVudCBjb3VsZCBub3QgYmUgcHJvY2Vzc2VkLiBQbGVhc2UgdHJ5IGFnYWluLiI7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiUGF5bWVudCBGYWlsZWQiOwogICAgICAgICAgICAgICAgICAgIGJvZHkgPSAiWW91ciBwYXltZW50IGNvdWxkIG5vdCBiZSBwcm9jZXNzZWQuIFBsZWFzZSB0cnkgYWdhaW4uIjsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQoKICAgICAgICAgICAgdmFyIHB1c2hEYXRhID0gbmV3CiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIHRpdGxlID0gdGl0bGUsCiAgICAgICAgICAgICAgICBib2R5ID0gYm9keSwKICAgICAgICAgICAgICAgIHVzZXJJZCA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGE/LnVzZXJJZCwKICAgICAgICAgICAgICAgIGRldmljZXMgPSByZWdpc3RlcmVkRGV2aWNlcywKICAgICAgICAgICAgICAgIHB1c2hUeXBlID0gInBheW1lbnQtbm90aWZpY2F0aW9uIiwKICAgICAgICAgICAgICAgIGRhdGEgPSBuZXcKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBwYXltZW50SWQgPSBwYXltZW50UmVzdWx0Py50cmFuc2FjdGlvbklkLAogICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3MgPSBwYXltZW50UmVzdWx0Py5zdWNjZXNzCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH07CgogICAgICAgICAgICBodHRwVGFzay5TZXRCb2R5KHB1c2hEYXRhKTsKICAgICAgICAgICAgaHR0cFRhc2suU2V0SGVhZGVycyhuZXcgRGljdGlvbmFyeTxzdHJpbmcsIHN0cmluZz8+CiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIFsiQ29udGVudC1UeXBlIl0gPSAiYXBwbGljYXRpb24vanNvbiIKICAgICAgICAgICAgfSk7CgogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgS2V5ID0gInB1c2gtZXJyb3IiLCBEYXRhID0gbmV3IHsgZXJyb3IgPSBleC5NZXNzYWdlIH0gfSk7CiAgICAgICAgfQogICAgfQoKICAgIHB1YmxpYyBhc3luYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgc3RhdHVzQ29kZSA9IGNvbnRleHQuQm9keT8uc3RhdHVzQ29kZSA/PyA1MDA7CiAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgIHsKICAgICAgICAgICAgS2V5ID0gc3RhdHVzQ29kZSA+PSAyMDAgJiYgc3RhdHVzQ29kZSA8IDMwMCA/ICJwdXNoLXN1Y2Nlc3MiIDogInB1c2gtZmFpbHVyZSIsCiAgICAgICAgICAgIERhdGEgPSBuZXcgeyBwdXNoU2VudCA9IHN0YXR1c0NvZGUgPj0gMjAwICYmIHN0YXR1c0NvZGUgPCAzMDAgfQogICAgICAgIH07CiAgICB9Cn0K" } } ], @@ -162,10 +142,6 @@ { "language": "en-US", "label": "Auto - Notifications Sent" - }, - { - "language": "tr-TR", - "label": "Otomatik - Bildirimler Gönderildi" } ], "schema": null, @@ -188,10 +164,6 @@ { "language": "en-US", "label": "Notification Process Complete" - }, - { - "language": "tr-TR", - "label": "Bildirim Süreci Tamamlandı" } ], "view": null, diff --git a/core/Workflows/payments/payment-process.json b/core/Workflows/payments/payment-process.json index 1e518e6..11c65ba 100644 --- a/core/Workflows/payments/payment-process.json +++ b/core/Workflows/payments/payment-process.json @@ -18,10 +18,6 @@ { "language": "en-US", "label": "Payment Processing" - }, - { - "language": "tr-TR", - "label": "Ödeme İşleme" } ], "functions": [], @@ -37,10 +33,6 @@ { "language": "en-US", "label": "Start Payment Processing" - }, - { - "language": "tr-TR", - "label": "Ödeme İşlemeyi Başlat" } ], "onExecutionTasks": [] @@ -55,10 +47,6 @@ { "language": "en-US", "label": "Payment Pending" - }, - { - "language": "tr-TR", - "label": "Ödeme Beklemede" } ], "view": null, @@ -75,10 +63,6 @@ { "language": "en-US", "label": "Scheduled - Payment Due Date/Time" - }, - { - "language": "tr-TR", - "label": "Zamanlanmış - Ödeme Vade Tarihi/Saati" } ], "schema": null, @@ -99,10 +83,6 @@ { "language": "en-US", "label": "Manual - Pay Now" - }, - { - "language": "tr-TR", - "label": "Manuel - Hemen Öde" } ], "schema": null, @@ -122,10 +102,6 @@ { "language": "en-US", "label": "ProcessPayment" - }, - { - "language": "tr-TR", - "label": "Ödeme İşle" } ], "view": null, @@ -156,10 +132,6 @@ { "language": "en-US", "label": "Payment Processed" - }, - { - "language": "tr-TR", - "label": "Ödeme İşlendi" } ], "schema": null, @@ -180,10 +152,6 @@ { "language": "en-US", "label": "Payment Error" - }, - { - "language": "tr-TR", - "label": "Ödeme Hatası" } ], "schema": null, @@ -206,10 +174,6 @@ { "language": "en-US", "label": "Failed" - }, - { - "language": "tr-TR", - "label": "Başarısız" } ], "view": null, @@ -226,10 +190,6 @@ { "language": "en-US", "label": "Auto - Retry Payment" - }, - { - "language": "tr-TR", - "label": "Otomatik - Ödemeyi Tekrar Dene" } ], "schema": null, @@ -250,10 +210,6 @@ { "language": "en-US", "label": "Manual - User Triggers Retry" - }, - { - "language": "tr-TR", - "label": "Manuel - Kullanıcı Yeniden Deneme Tetikler" } ], "schema": null, @@ -271,10 +227,6 @@ { "language": "en-US", "label": "Max Retries Reached" - }, - { - "language": "tr-TR", - "label": "Maksimum Yeniden Deneme Sayısına Ulaşıldı" } ], "schema": null, @@ -295,10 +247,6 @@ { "language": "en-US", "label": "Main Workflow Deactivated" - }, - { - "language": "tr-TR", - "label": "Ana İş Akışı Deaktive Edildi" } ], "schema": null, @@ -321,10 +269,6 @@ { "language": "en-US", "label": "Retry" - }, - { - "language": "tr-TR", - "label": "Yeniden Dene" } ], "view": null, @@ -355,10 +299,6 @@ { "language": "en-US", "label": "Auto - Retry Logic" - }, - { - "language": "tr-TR", - "label": "Otomatik - Yeniden Deneme Mantığı" } ], "schema": null, @@ -381,10 +321,6 @@ { "language": "en-US", "label": "Success" - }, - { - "language": "tr-TR", - "label": "Başarılı" } ], "view": null, @@ -402,10 +338,6 @@ { "language": "en-US", "label": "Cancelled" - }, - { - "language": "tr-TR", - "label": "İptal Edildi" } ], "view": null, @@ -416,4 +348,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/payments/scheduled-payments-workflow.json b/core/Workflows/payments/scheduled-payments-workflow.json index 3ca0133..c493218 100644 --- a/core/Workflows/payments/scheduled-payments-workflow.json +++ b/core/Workflows/payments/scheduled-payments-workflow.json @@ -18,10 +18,6 @@ { "language": "en-US", "label": "Scheduled Payments Workflow" - }, - { - "language": "tr-TR", - "label": "Zamanlanmış Ödemeler İş Akışı" } ], "functions": [ @@ -44,10 +40,6 @@ { "language": "en-US", "label": "Start Payment Setup" - }, - { - "language": "tr-TR", - "label": "Ödeme Kurulum Başlat" } ], "onExecutionTasks": [] @@ -62,10 +54,6 @@ { "language": "en-US", "label": "Manual - Save Payment Configuration" - }, - { - "language": "tr-TR", - "label": "Manuel - Ödeme Konfigürasyonu Kaydet" } ], "view": null, @@ -96,10 +84,6 @@ { "language": "en-US", "label": "Auto - Payment Saved Successfully" - }, - { - "language": "tr-TR", - "label": "Otomatik - Ödeme Başarıyla Kaydedildi" } ], "schema": null, @@ -120,10 +104,6 @@ { "language": "en-US", "label": "Configuration Failed - Retry" - }, - { - "language": "tr-TR", - "label": "Konfigürasyon Başarısız - Tekrar Dene" } ], "schema": null, @@ -146,10 +126,6 @@ { "language": "en-US", "label": "Active" - }, - { - "language": "tr-TR", - "label": "Aktif" } ], "view": null, @@ -192,10 +168,6 @@ { "language": "en-US", "label": "Manual - Deactivate Payment" - }, - { - "language": "tr-TR", - "label": "Manuel - Ödemeyi Deaktive Et" } ], "schema": null, @@ -213,10 +185,6 @@ { "language": "en-US", "label": "Auto - Payment Process Complete (Success & More Payments Remaining)" - }, - { - "language": "tr-TR", - "label": "Otomatik - Ödeme Süreci Tamamlandı (Başarılı & Daha Fazla Ödeme Var)" } ], "schema": null, @@ -237,10 +205,6 @@ { "language": "en-US", "label": "Auto - All Payments Complete (Count/Date/Pattern Finished)" - }, - { - "language": "tr-TR", - "label": "Otomatik - Tüm Ödemeler Tamamlandı (Sayı/Tarih/Desen Bitti)" } ], "schema": null, @@ -263,10 +227,6 @@ { "language": "en-US", "label": "Deactive" - }, - { - "language": "tr-TR", - "label": "Deaktif" } ], "view": null, @@ -297,10 +257,6 @@ { "language": "en-US", "label": "Manual - Reactivate Payment" - }, - { - "language": "tr-TR", - "label": "Manuel - Ödemeyi Reaktive Et" } ], "schema": null, @@ -318,10 +274,6 @@ { "language": "en-US", "label": "Manual - Update Payment" - }, - { - "language": "tr-TR", - "label": "Manuel - Ödemeyi Güncelle" } ], "schema": null, @@ -339,10 +291,6 @@ { "language": "en-US", "label": "Manual - Delete Payment" - }, - { - "language": "tr-TR", - "label": "Manuel - Ödemeyi Sil" } ], "schema": null, @@ -362,10 +310,6 @@ { "language": "en-US", "label": "Payment Cycle Check" - }, - { - "language": "tr-TR", - "label": "Ödeme Döngüsü Kontrolü" } ], "view": null, @@ -394,10 +338,6 @@ { "language": "en-US", "label": "Auto - Continue Payment Cycle" - }, - { - "language": "tr-TR", - "label": "Otomatik - Ödeme Döngüsünü Devam Ettir" } ], "schema": null, @@ -420,10 +360,6 @@ { "language": "en-US", "label": "Finished" - }, - { - "language": "tr-TR", - "label": "Tamamlandı" } ], "view": null, @@ -455,10 +391,6 @@ { "language": "en-US", "label": "Terminated" - }, - { - "language": "tr-TR", - "label": "Sonlandırıldı" } ], "view": null, @@ -469,4 +401,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/payments/src/SendPaymentNotificationSmsMapping.csx b/core/Workflows/payments/src/SendPaymentNotificationSmsMapping.csx index 89345ae..a80b4e0 100644 --- a/core/Workflows/payments/src/SendPaymentNotificationSmsMapping.csx +++ b/core/Workflows/payments/src/SendPaymentNotificationSmsMapping.csx @@ -24,7 +24,7 @@ public class SendPaymentNotificationSmsMapping : IMapping // Determine message based on payment status var message = ""; - var language = context.Instance?.Data?.language ?? "tr-TR"; + var language = context.Instance?.Data?.language ?? "en-US"; if (paymentResult?.success == true) { @@ -34,7 +34,7 @@ public class SendPaymentNotificationSmsMapping : IMapping } else { - message = $"Ödeme başarılı! Miktar: {paymentResult?.amount} {paymentResult?.currency}. Referans: {paymentResult?.transactionId}"; + message = $"Payment successful! Amount: {paymentResult?.amount} {paymentResult?.currency}. Reference: {paymentResult?.transactionId}"; } } else @@ -45,7 +45,7 @@ public class SendPaymentNotificationSmsMapping : IMapping } else { - message = $"Ödeme başarısız. Neden: {paymentResult?.errorMessage ?? "Bilinmeyen sebep"}. Lütfen ödeme bilgilerinizi kontrol edin."; + message = $"Payment failed. Reason: {paymentResult?.errorMessage ?? "Unknown error"}. Please check your payment details."; } } diff --git a/core/Workflows/payments/src/SendPaymentPushNotificationMapping.csx b/core/Workflows/payments/src/SendPaymentPushNotificationMapping.csx index 2c5c533..af25c22 100644 --- a/core/Workflows/payments/src/SendPaymentPushNotificationMapping.csx +++ b/core/Workflows/payments/src/SendPaymentPushNotificationMapping.csx @@ -28,9 +28,9 @@ public class SendPaymentPushNotificationMapping : IMapping var paymentResult = context.Instance?.Data?.paymentResult; var user = context.Instance?.Data?.user; var registeredDevices = context.Instance?.Data?.registeredDevices; - var language = user?.language ?? "tr-TR"; + var language = user?.language ?? "en-US"; - // Generate push message + // Generate push message (English baseline; localize via explicit language tag if needed later) var title = ""; var body = ""; if (paymentResult?.success == true) @@ -40,11 +40,11 @@ public class SendPaymentPushNotificationMapping : IMapping title = "Payment Successful"; body = $"Your payment of {paymentResult?.amount} {paymentResult?.currency} has been processed successfully."; } - else - { - title = "Ödeme Başarılı"; - body = $"{paymentResult?.amount} {paymentResult?.currency} ödemeniz başarıyla işleme alındı."; - } + else + { + title = "Payment Successful"; + body = $"Your payment of {paymentResult?.amount} {paymentResult?.currency} has been processed successfully."; + } } else { @@ -55,8 +55,8 @@ public class SendPaymentPushNotificationMapping : IMapping } else { - title = "Ödeme Başarısız"; - body = "Ödemeniz gerçekleştirilemedi. Lütfen tekrar deneyin."; + title = "Payment Failed"; + body = "Your payment could not be processed. Please try again."; } } diff --git a/core/Workflows/schema-data/schema-data-test-workflow.json b/core/Workflows/schema-data/schema-data-test-workflow.json index 487a093..814413f 100644 --- a/core/Workflows/schema-data/schema-data-test-workflow.json +++ b/core/Workflows/schema-data/schema-data-test-workflow.json @@ -21,10 +21,6 @@ { "language": "en-US", "label": "Schema & Data Management Integration Test" - }, - { - "language": "tr-TR", - "label": "Schema ve Data Yonetimi Entegrasyon Testi" } ], "functions": [], @@ -48,10 +44,6 @@ { "language": "en-US", "label": "Update Instance Data (updateData $self)" - }, - { - "language": "tr-TR", - "label": "Instance Datasini Guncelle (updateData $self)" } ] }, @@ -64,10 +56,6 @@ { "language": "en-US", "label": "Start Schema Test" - }, - { - "language": "tr-TR", - "label": "Schema Testini Baslat" } ], "onExecutionTasks": [ @@ -96,10 +84,6 @@ { "language": "en-US", "label": "Data Initialized (Master Schema Active)" - }, - { - "language": "tr-TR", - "label": "Data Baslatildi (Ana Schema Aktif)" } ], "view": null, @@ -116,10 +100,6 @@ { "language": "en-US", "label": "Confirm with Transition Schema" - }, - { - "language": "tr-TR", - "label": "Transition Schema ile Onayla" } ], "schema": { @@ -154,10 +134,6 @@ { "language": "en-US", "label": "Skip to No-Schema State" - }, - { - "language": "tr-TR", - "label": "Schemasiz Duruma Atla" } ], "schema": null, @@ -175,10 +151,6 @@ { "language": "en-US", "label": "Schema Validated State" - }, - { - "language": "tr-TR", - "label": "Schema Dogrulanmis Durum" } ], "view": null, @@ -195,10 +167,6 @@ { "language": "en-US", "label": "Move to No-Schema State" - }, - { - "language": "tr-TR", - "label": "Schemasiz Duruma Gec" } ], "schema": null, @@ -216,10 +184,6 @@ { "language": "en-US", "label": "No Schema State (Free-form transitions)" - }, - { - "language": "tr-TR", - "label": "Schemasiz Durum (Serbest gecisler)" } ], "view": null, @@ -236,10 +200,6 @@ { "language": "en-US", "label": "Complete Schema Test" - }, - { - "language": "tr-TR", - "label": "Schema Testini Tamamla" } ], "schema": null, @@ -257,10 +217,6 @@ { "language": "en-US", "label": "Schema Test Completed (Final/Success)" - }, - { - "language": "tr-TR", - "label": "Schema Testi Tamamlandi (Final/Basarili)" } ], "view": null, @@ -271,4 +227,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/smoke-runtime-check/smoke-runtime-check-workflow.json b/core/Workflows/smoke-runtime-check/smoke-runtime-check-workflow.json index 4f33da7..d81bc27 100644 --- a/core/Workflows/smoke-runtime-check/smoke-runtime-check-workflow.json +++ b/core/Workflows/smoke-runtime-check/smoke-runtime-check-workflow.json @@ -17,10 +17,6 @@ { "language": "en-US", "label": "Smoke Runtime Check (minimal manual flow)" - }, - { - "language": "tr-TR", - "label": "Runtime Smoke Testi" } ], "functions": [], @@ -36,10 +32,6 @@ { "language": "en-US", "label": "Start" - }, - { - "language": "tr-TR", - "label": "Baslat" } ], "onExecutionTasks": [] @@ -54,10 +46,6 @@ { "language": "en-US", "label": "Smoke Start" - }, - { - "language": "tr-TR", - "label": "Baslangic" } ], "view": null, @@ -74,10 +62,6 @@ { "language": "en-US", "label": "Finish (manual only)" - }, - { - "language": "tr-TR", - "label": "Bitir" } ], "schema": null, @@ -96,10 +80,6 @@ { "language": "en-US", "label": "Smoke Done" - }, - { - "language": "tr-TR", - "label": "Tamamlandi" } ], "view": null, diff --git a/core/Workflows/subflow-orchestration/subflow-orchestration-child.json b/core/Workflows/subflow-orchestration/subflow-orchestration-child.json index be79a2f..4808e42 100644 --- a/core/Workflows/subflow-orchestration/subflow-orchestration-child.json +++ b/core/Workflows/subflow-orchestration/subflow-orchestration-child.json @@ -18,10 +18,6 @@ { "language": "en-US", "label": "SubFlow Orchestration Child" - }, - { - "language": "tr-TR", - "label": "Alt Akis Orkestrasyon Cocuk" } ], "functions": [], @@ -37,10 +33,6 @@ { "language": "en-US", "label": "Shared Child Update ($self)" - }, - { - "language": "tr-TR", - "label": "Paylasimli Child Guncelleme ($self)" } ], "availableIn": [ @@ -72,10 +64,6 @@ { "language": "en-US", "label": "Cancel Child" - }, - { - "language": "tr-TR", - "label": "Child Iptal" } ] }, @@ -88,10 +76,6 @@ { "language": "en-US", "label": "Update Parent Data from Child" - }, - { - "language": "tr-TR", - "label": "Child'dan Parent Data Guncelle" } ], "onExecutionTasks": [ @@ -119,10 +103,6 @@ { "language": "en-US", "label": "Start Child" - }, - { - "language": "tr-TR", - "label": "Cocugu Baslat" } ] }, @@ -136,10 +116,6 @@ { "language": "en-US", "label": "Child Initial" - }, - { - "language": "tr-TR", - "label": "Cocuk Baslangic" } ], "view": null, @@ -170,10 +146,6 @@ { "language": "en-US", "label": "Auto to Child SubFlow State" - }, - { - "language": "tr-TR", - "label": "Otomatik Alt Akis Durumuna" } ], "rule": { @@ -193,10 +165,6 @@ { "language": "en-US", "label": "Child SubFlow State" - }, - { - "language": "tr-TR", - "label": "Cocuk Alt Akis Durumu" } ], "view": null, @@ -225,10 +193,6 @@ { "language": "en-US", "label": "Auto to Child Completed" - }, - { - "language": "tr-TR", - "label": "Otomatik Cocuk Tamamlandi" } ], "rule": { @@ -248,10 +212,6 @@ { "language": "en-US", "label": "Child Completed" - }, - { - "language": "tr-TR", - "label": "Cocuk Tamamlandi" } ], "view": null, @@ -269,10 +229,6 @@ { "language": "en-US", "label": "Child Cancelled" - }, - { - "language": "tr-TR", - "label": "Cocuk Iptal" } ], "view": null, @@ -283,4 +239,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/subflow-orchestration/subflow-orchestration-grandchild.json b/core/Workflows/subflow-orchestration/subflow-orchestration-grandchild.json index 1a1d8a4..7f63964 100644 --- a/core/Workflows/subflow-orchestration/subflow-orchestration-grandchild.json +++ b/core/Workflows/subflow-orchestration/subflow-orchestration-grandchild.json @@ -17,10 +17,6 @@ { "language": "en-US", "label": "SubFlow Orchestration Grandchild" - }, - { - "language": "tr-TR", - "label": "Alt Akis Orkestrasyon Torun" } ], "functions": [], @@ -36,10 +32,6 @@ { "language": "en-US", "label": "Cancel Grandchild" - }, - { - "language": "tr-TR", - "label": "Grandchild Iptal" } ] }, @@ -52,10 +44,6 @@ { "language": "en-US", "label": "Start Grandchild" - }, - { - "language": "tr-TR", - "label": "Torunu Baslat" } ] }, @@ -69,10 +57,6 @@ { "language": "en-US", "label": "Grandchild Initial" - }, - { - "language": "tr-TR", - "label": "Torun Baslangic" } ], "view": null, @@ -103,10 +87,6 @@ { "language": "en-US", "label": "Complete Grandchild" - }, - { - "language": "tr-TR", - "label": "Torunu Tamamla" } ], "onExecutionTasks": [] @@ -122,10 +102,6 @@ { "language": "en-US", "label": "Grandchild Completed" - }, - { - "language": "tr-TR", - "label": "Torun Tamamlandi" } ], "view": null, @@ -157,10 +133,6 @@ { "language": "en-US", "label": "Grandchild Cancelled" - }, - { - "language": "tr-TR", - "label": "Torun Iptal" } ], "view": null, @@ -171,4 +143,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json b/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json index 64eeb1e..4b990c2 100644 --- a/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json +++ b/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json @@ -18,10 +18,6 @@ { "language": "en-US", "label": "SubFlow Orchestration Parent" - }, - { - "language": "tr-TR", - "label": "Alt Akis Orkestrasyon Ebeveyn" } ], "functions": [], @@ -37,10 +33,6 @@ { "language": "en-US", "label": "Shared Update Data" - }, - { - "language": "tr-TR", - "label": "Paylasimli Veri Guncelle" } ], "availableIn": [ @@ -72,10 +64,6 @@ { "language": "en-US", "label": "Cancel Parent" - }, - { - "language": "tr-TR", - "label": "Ebeveyni Iptal Et" } ] }, @@ -88,10 +76,6 @@ { "language": "en-US", "label": "Start Parent Orchestration" - }, - { - "language": "tr-TR", - "label": "Ebeveyn Orkestrasyonu Baslat" } ], "onExecutionTasks": [ @@ -120,10 +104,6 @@ { "language": "en-US", "label": "Parent Initial" - }, - { - "language": "tr-TR", - "label": "Ebeveyn Baslangic" } ], "view": null, @@ -140,10 +120,6 @@ { "language": "en-US", "label": "Auto to Parent SubFlow State" - }, - { - "language": "tr-TR", - "label": "Otomatik Alt Akis Durumuna" } ], "rule": { @@ -163,10 +139,6 @@ { "language": "en-US", "label": "Parent SubFlow State" - }, - { - "language": "tr-TR", - "label": "Ebeveyn Alt Akis Durumu" } ], "view": null, @@ -195,10 +167,6 @@ { "language": "en-US", "label": "Auto to Parent Completed" - }, - { - "language": "tr-TR", - "label": "Otomatik Ebeveyn Tamamlandi" } ], "rule": { @@ -218,10 +186,6 @@ { "language": "en-US", "label": "Parent Completed" - }, - { - "language": "tr-TR", - "label": "Ebeveyn Tamamlandi" } ], "view": null, @@ -239,10 +203,6 @@ { "language": "en-US", "label": "Parent Cancelled" - }, - { - "language": "tr-TR", - "label": "Ebeveyn Iptal" } ], "view": null, @@ -253,4 +213,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/subflow-test/busy-subprocess-workflow.json b/core/Workflows/subflow-test/busy-subprocess-workflow.json index 794a2f5..a3f3c36 100644 --- a/core/Workflows/subflow-test/busy-subprocess-workflow.json +++ b/core/Workflows/subflow-test/busy-subprocess-workflow.json @@ -17,10 +17,6 @@ { "language": "en-US", "label": "Busy State SubProcess Workflow" - }, - { - "language": "tr-TR", - "label": "Meşgul Durumu Alt İşlem Akışı" } ], "functions": [], @@ -36,10 +32,6 @@ { "language": "en-US", "label": "Start SubProcess" - }, - { - "language": "tr-TR", - "label": "Alt İşlemi Başlat" } ], "onExecutionTasks": [ @@ -68,10 +60,6 @@ { "language": "en-US", "label": "Processing" - }, - { - "language": "tr-TR", - "label": "İşleniyor" } ], "view": null, @@ -88,10 +76,6 @@ { "language": "en-US", "label": "Complete Processing (Auto)" - }, - { - "language": "tr-TR", - "label": "İşlemi Tamamla (Otomatik)" } ], "schema": null, @@ -114,10 +98,6 @@ { "language": "en-US", "label": "Trigger Parent Workflow" - }, - { - "language": "tr-TR", - "label": "Üst Akışı Tetikle" } ], "view": null, @@ -148,10 +128,6 @@ { "language": "en-US", "label": "Finish SubProcess (Auto)" - }, - { - "language": "tr-TR", - "label": "Alt İşlemi Bitir (Otomatik)" } ], "schema": null, @@ -174,10 +150,6 @@ { "language": "en-US", "label": "SubProcess Completed" - }, - { - "language": "tr-TR", - "label": "Alt İşlem Tamamlandı" } ], "view": null, @@ -188,4 +160,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/subflow-test/subflow-view-test-child.json b/core/Workflows/subflow-test/subflow-view-test-child.json index 790b4db..ec889d7 100644 --- a/core/Workflows/subflow-test/subflow-view-test-child.json +++ b/core/Workflows/subflow-test/subflow-view-test-child.json @@ -27,10 +27,6 @@ { "language": "en-US", "label": "SubFlow View Test - Child Workflow" - }, - { - "language": "tr-TR", - "label": "Alt Akış Görünüm Testi - Alt Akış" } ], "cancel": { @@ -70,10 +66,6 @@ { "language": "en-US", "label": "Child Shared Update (issue #425)" - }, - { - "language": "tr-TR", - "label": "Alt Akış Paylaşımlı Güncelleme (#425)" } ], "schema": null, @@ -106,10 +98,6 @@ { "language": "en-US", "label": "Start Child Workflow" - }, - { - "language": "tr-TR", - "label": "Alt Akışı Başlat" } ], "onExecutionTasks": [ @@ -138,10 +126,6 @@ { "language": "en-US", "label": "Child Initial State" - }, - { - "language": "tr-TR", - "label": "Alt Akış Başlangıç Durumu" } ], "view": { @@ -169,10 +153,6 @@ { "language": "en-US", "label": "Start Grandchild SubFlow" - }, - { - "language": "tr-TR", - "label": "Torun Alt Akışı Başlat" } ], "roles": [ @@ -198,10 +178,6 @@ { "language": "en-US", "label": "Waiting for Grandchild SubFlow" - }, - { - "language": "tr-TR", - "label": "Torun Alt Akış Bekleniyor" } ], "view": null, @@ -276,10 +252,6 @@ { "language": "en-US", "label": "Grandchild SubFlow Completed" - }, - { - "language": "tr-TR", - "label": "Torun Alt Akış Tamamlandı" } ], "schema": null, @@ -302,10 +274,6 @@ { "language": "en-US", "label": "After Grandchild SubFlow State" - }, - { - "language": "tr-TR", - "label": "Torun Alt Akış Sonrası Durumu" } ], "view": { @@ -333,10 +301,6 @@ { "language": "en-US", "label": "Complete Child Workflow" - }, - { - "language": "tr-TR", - "label": "Alt Akışı Tamamla" } ], "schema": null, @@ -370,10 +334,6 @@ { "language": "en-US", "label": "Child Workflow Completed" - }, - { - "language": "tr-TR", - "label": "Alt Akış Tamamlandı" } ], "view": null, @@ -391,10 +351,6 @@ { "language": "en-US", "label": "Child Workflow Cancelled" - }, - { - "language": "tr-TR", - "label": "Çocuk Akış İptal Edildi" } ], "view": null, @@ -405,4 +361,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/subflow-test/subflow-view-test-grandchild.json b/core/Workflows/subflow-test/subflow-view-test-grandchild.json index ac1115f..6495245 100644 --- a/core/Workflows/subflow-test/subflow-view-test-grandchild.json +++ b/core/Workflows/subflow-test/subflow-view-test-grandchild.json @@ -20,10 +20,6 @@ { "language": "en-US", "label": "SubFlow View Test - Grandchild Workflow" - }, - { - "language": "tr-TR", - "label": "Alt Akış Görünüm Testi - Torun Akış" } ], "functions": [], @@ -46,10 +42,6 @@ { "language": "en-US", "label": "Start Grandchild Workflow" - }, - { - "language": "tr-TR", - "label": "Torun Akışı Başlat" } ], "onExecutionTasks": [ @@ -78,10 +70,6 @@ { "language": "en-US", "label": "Grandchild Workflow Active" - }, - { - "language": "tr-TR", - "label": "Torun Akış Aktif" } ], "view": { @@ -119,10 +107,6 @@ { "language": "en-US", "label": "Enter Busy State" - }, - { - "language": "tr-TR", - "label": "Meşgul Durumuna Geç" } ], "roles": [ @@ -156,10 +140,6 @@ { "language": "en-US", "label": "Grandchild Busy - Processing" - }, - { - "language": "tr-TR", - "label": "Torun Meşgul - İşleniyor" } ], "view": null, @@ -190,10 +170,6 @@ { "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, @@ -213,10 +189,6 @@ { "language": "en-US", "label": "Grandchild After Busy State" - }, - { - "language": "tr-TR", - "label": "Torun Meşgul Sonrası" } ], "view": null, @@ -233,10 +205,6 @@ { "language": "en-US", "label": "Complete Grandchild Workflow" - }, - { - "language": "tr-TR", - "label": "Torun Akışı Tamamla" } ], "schema": null, @@ -270,10 +238,6 @@ { "language": "en-US", "label": "Grandchild Workflow Completed" - }, - { - "language": "tr-TR", - "label": "Torun Akış Tamamlandı" } ], "view": null, @@ -284,4 +248,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/subflow-test/subflow-view-test-parent.json b/core/Workflows/subflow-test/subflow-view-test-parent.json index 6d8bd2f..1b2e1bc 100644 --- a/core/Workflows/subflow-test/subflow-view-test-parent.json +++ b/core/Workflows/subflow-test/subflow-view-test-parent.json @@ -18,10 +18,6 @@ { "language": "en-US", "label": "SubFlow View Test - Parent Workflow" - }, - { - "language": "tr-TR", - "label": "Alt Akış Görünüm Testi - Ana Akış" } ], "cancel": { @@ -61,10 +57,6 @@ { "language": "en-US", "label": "Parent Shared Update (issue #425)" - }, - { - "language": "tr-TR", - "label": "Üst Akış Paylaşımlı Güncelleme (#425)" } ], "schema": null, @@ -97,10 +89,6 @@ { "language": "en-US", "label": "Start Parent Workflow" - }, - { - "language": "tr-TR", - "label": "Ana Akışı Başlat" } ], "onExecutionTasks": [ @@ -129,10 +117,6 @@ { "language": "en-US", "label": "Parent Initial State" - }, - { - "language": "tr-TR", - "label": "Ana Akış Başlangıç Durumu" } ], "view": { @@ -184,10 +168,6 @@ { "language": "en-US", "label": "Start SubFlow" - }, - { - "language": "tr-TR", - "label": "Alt Akışı Başlat" } ], "schema": null, @@ -207,10 +187,6 @@ { "language": "en-US", "label": "Waiting for SubFlow" - }, - { - "language": "tr-TR", - "label": "Alt Akış Bekleniyor" } ], "view": null, @@ -290,10 +266,6 @@ { "language": "en-US", "label": "SubFlow Completed" - }, - { - "language": "tr-TR", - "label": "Alt Akış Tamamlandı" } ], "schema": null, @@ -316,10 +288,6 @@ { "language": "en-US", "label": "After SubFlow State" - }, - { - "language": "tr-TR", - "label": "Alt Akış Sonrası Durumu" } ], "view": { @@ -347,10 +315,6 @@ { "language": "en-US", "label": "Proceed After SubFlow" - }, - { - "language": "tr-TR", - "label": "Alt Akış Sonrası Devam Et" } ], "schema": null, @@ -384,10 +348,6 @@ { "language": "en-US", "label": "Parent Workflow Completed" - }, - { - "language": "tr-TR", - "label": "Ana Akış Tamamlandı" } ], "view": null, @@ -405,10 +365,6 @@ { "language": "en-US", "label": "Parent Workflow Cancelled" - }, - { - "language": "tr-TR", - "label": "Ana Akış İptal Edildi" } ], "view": null, @@ -419,4 +375,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/task-execution/extended-tasks-test-workflow.json b/core/Workflows/task-execution/extended-tasks-test-workflow.json index 493a98f..078141c 100644 --- a/core/Workflows/task-execution/extended-tasks-test-workflow.json +++ b/core/Workflows/task-execution/extended-tasks-test-workflow.json @@ -18,10 +18,6 @@ { "language": "en-US", "label": "Extended Tasks (Dapr) Test Workflow" - }, - { - "language": "tr-TR", - "label": "Genisletilmis Gorevler (Dapr) Test Is Akisi" } ], "functions": [], @@ -37,10 +33,6 @@ { "language": "en-US", "label": "Start Extended Tasks Test" - }, - { - "language": "tr-TR", - "label": "Genisletilmis Gorev Testini Baslat" } ], "onExecutionTasks": [] @@ -55,10 +47,6 @@ { "language": "en-US", "label": "Initialize" - }, - { - "language": "tr-TR", - "label": "Baslangic" } ], "view": null, @@ -89,10 +77,6 @@ { "language": "en-US", "label": "Auto to Dapr HTTP" - }, - { - "language": "tr-TR", - "label": "Dapr HTTP'ye Otomatik" } ], "rule": { @@ -112,10 +96,6 @@ { "language": "en-US", "label": "Dapr HTTP Task" - }, - { - "language": "tr-TR", - "label": "Dapr HTTP Gorevi" } ], "view": null, @@ -146,10 +126,6 @@ { "language": "en-US", "label": "Auto to Dapr Service" - }, - { - "language": "tr-TR", - "label": "Dapr Service'e Otomatik" } ], "rule": { @@ -169,10 +145,6 @@ { "language": "en-US", "label": "Dapr Service Task" - }, - { - "language": "tr-TR", - "label": "Dapr Service Gorevi" } ], "view": null, @@ -203,10 +175,6 @@ { "language": "en-US", "label": "Auto to Dapr PubSub" - }, - { - "language": "tr-TR", - "label": "Dapr PubSub'a Otomatik" } ], "rule": { @@ -226,10 +194,6 @@ { "language": "en-US", "label": "Dapr PubSub Task" - }, - { - "language": "tr-TR", - "label": "Dapr PubSub Gorevi" } ], "view": null, @@ -245,7 +209,7 @@ }, "mapping": { "location": "./src/extended-tasks-test-workflow/DaprPubSubMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRleHQuSnNvbjsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBEYXByUHViU3ViTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIC8vLyA8c3VtbWFyeT4KICAgIC8vLyBUYXNrRXhlY3V0aW9uRW5naW5lLCB0YXNrIHNvbnVjdW51IEpzb25TZXJpYWxpemVyIGlsZSB5YXphcmtlbiBFeHBhbmRvT2JqZWN0IGnDp2luZGVraQogICAgLy8vIEpzb25FbGVtZW50IHJlZmVyYW5zbGFyxLFuxLEgc2VyaWxlxZ90aXJlbWl5b3IgKCJPcGVyYXRpb24gaXMgbm90IHZhbGlkIGR1ZSB0byB0aGUgY3VycmVudCBzdGF0ZSBvZiB0aGUgb2JqZWN0IikuCiAgICAvLy8gY29udGV4dC5Cb2R5LmlzU3VjY2VzcyAvIHN0YXR1c0NvZGUgcnVudGltZSdkYSBKc29uRWxlbWVudCBvbGFyYWsgZ2VsZWJpbGlyIOKAlCBDTFIgdGlwaW5lIMOnZXZpcml5b3J1ei4KICAgIC8vLyA8L3N1bW1hcnk+CiAgICBwcml2YXRlIHN0YXRpYyBib29sIENvZXJjZUJvb2wob2JqZWN0IG8pCiAgICB7CiAgICAgICAgaWYgKG8gPT0gbnVsbCkKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIGlmIChvIGlzIGJvb2wgYikKICAgICAgICAgICAgcmV0dXJuIGI7CiAgICAgICAgaWYgKG8gaXMgSnNvbkVsZW1lbnQgamUpCiAgICAgICAgewogICAgICAgICAgICByZXR1cm4gamUuVmFsdWVLaW5kIHN3aXRjaAogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBKc29uVmFsdWVLaW5kLlRydWUgPT4gdHJ1ZSwKICAgICAgICAgICAgICAgIEpzb25WYWx1ZUtpbmQuRmFsc2UgPT4gZmFsc2UsCiAgICAgICAgICAgICAgICBKc29uVmFsdWVLaW5kLlN0cmluZyA9PiBib29sLlRyeVBhcnNlKGplLkdldFN0cmluZygpLCBvdXQgdmFyIHApICYmIHAsCiAgICAgICAgICAgICAgICBKc29uVmFsdWVLaW5kLk51bWJlciA9PiBqZS5UcnlHZXRJbnQzMihvdXQgdmFyIG4pID8gbiAhPSAwIDogZmFsc2UsCiAgICAgICAgICAgICAgICBfID0+IGZhbHNlLAogICAgICAgICAgICB9OwogICAgICAgIH0KICAgICAgICBpZiAobyBpcyBzdHJpbmcgcykKICAgICAgICAgICAgcmV0dXJuIGJvb2wuVHJ5UGFyc2Uocywgb3V0IHZhciB4KSAmJiB4OwogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgcmV0dXJuIENvbnZlcnQuVG9Cb29sZWFuKG8pOwogICAgICAgIH0KICAgICAgICBjYXRjaAogICAgICAgIHsKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KICAgIH0KCiAgICBwcml2YXRlIHN0YXRpYyBpbnQ/IENvZXJjZUludDMyKG9iamVjdCBvKQogICAgewogICAgICAgIGlmIChvID09IG51bGwpCiAgICAgICAgICAgIHJldHVybiBudWxsOwogICAgICAgIGlmIChvIGlzIGludCBpKQogICAgICAgICAgICByZXR1cm4gaTsKICAgICAgICBpZiAobyBpcyBsb25nIGwpCiAgICAgICAgICAgIHJldHVybiBjaGVja2VkKChpbnQpbCk7CiAgICAgICAgaWYgKG8gaXMgSnNvbkVsZW1lbnQgamUgJiYgamUuVmFsdWVLaW5kID09IEpzb25WYWx1ZUtpbmQuTnVtYmVyICYmIGplLlRyeUdldEludDMyKG91dCB2YXIgbikpCiAgICAgICAgICAgIHJldHVybiBuOwogICAgICAgIGlmIChvIGlzIHN0cmluZyBzICYmIGludC5UcnlQYXJzZShzLCBvdXQgdmFyIHApKQogICAgICAgICAgICByZXR1cm4gcDsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIHJldHVybiBDb252ZXJ0LlRvSW50MzIobyk7CiAgICAgICAgfQogICAgICAgIGNhdGNoCiAgICAgICAgewogICAgICAgICAgICByZXR1cm4gbnVsbDsKICAgICAgICB9CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBwdWJzdWJUYXNrID0gKHRhc2sgYXMgRGFwclB1YlN1YlRhc2spITsKICAgICAgICBpZiAocHVic3ViVGFzayA9PSBudWxsKQogICAgICAgICAgICB0aHJvdyBuZXcgSW52YWxpZE9wZXJhdGlvbkV4Y2VwdGlvbigiVGFzayBtdXN0IGJlIGEgRGFwclB1YlN1YlRhc2siKTsKCiAgICAgICAgLy8gVmF1bHQndGEgRGFwclB1YlN1Yk5hbWUgdmFyc2Egb251IGt1bGxhbiwgeW9rc2EgdGFzayBKU09OJ2Rha2kgZGVmYXVsdCAodGVzdC1wdWJzdWIpIGdlY2VybGkga2FsaXIuCiAgICAgICAgLy8gQm95bGVjZSBkZXZlbG9wbWVudCd0ZSBWYXVsdCBjb25maWcnaSBvcHNpeW9uZWw7IGZhcmtsaSBvcnRhbWRhIChvcm5lZ2luIHZuZXh0LXB1YnN1YikKICAgICAgICAvLyBzYWRlY2UgVmF1bHQnYSBrZXkgZWtsZW1layB5ZXRlcmxpLgogICAgICAgIHN0cmluZyB2YXVsdFZhbHVlID0gR2V0Q29uZmlnVmFsdWUoIkRhcHJQdWJTdWJOYW1lIik7CiAgICAgICAgaWYgKCFzdHJpbmcuSXNOdWxsT3JXaGl0ZVNwYWNlKHZhdWx0VmFsdWUpKQogICAgICAgIHsKICAgICAgICAgICAgcHVic3ViVGFzay5TZXRQdWJTdWJOYW1lKHZhdWx0VmFsdWUpOwogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkRhcHJQdWJTdWJNYXBwaW5nOiBWYXVsdCBvdmVycmlkZSBwdWJTdWJOYW1lID0ge3ZhdWx0VmFsdWV9Iik7CiAgICAgICAgfQogICAgICAgIGVsc2UKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKAogICAgICAgICAgICAgICAgJCJEYXByUHViU3ViTWFwcGluZzogVmF1bHQndGEgRGFwclB1YlN1Yk5hbWUgeW9rLCB0YXNrIEpTT04gZGVmYXVsdCBrdWxsYW5pbGl5b3IiCiAgICAgICAgICAgICk7CiAgICAgICAgfQoKICAgICAgICAvLyBQdWJsaXNoIGljaW4gYGRhdGFgIG51bGwgb2xhbWF6IChBcmd1bWVudE51bGxFeGNlcHRpb24pLiBUYXNrIEpTT04nZGEgZGF0YSBvbHNhIGJpbGUKICAgICAgICAvLyBlc2tpIHB1Ymxpc2ggLyBzaXJhc2kgecO8esO8bmRlbiBnw7x2ZW4gdmVybWV6OyBtYXBwaW5nIGlsZSBnYXJhbnRpIGV0LgogICAgICAgIHZhciBpbnN0YW5jZURhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyBtZXNzYWdlRGF0YSA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgbWVzc2FnZURhdGEuZXZlbnRUeXBlID0gIkludGVncmF0aW9uVGVzdCI7CiAgICAgICAgbWVzc2FnZURhdGEuc291cmNlID0gImV4dGVuZGVkLXRhc2tzLXRlc3Qtd29ya2Zsb3ciOwogICAgICAgIG1lc3NhZ2VEYXRhLnRpbWVzdGFtcCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShpbnN0YW5jZURhdGEsICJ0ZXN0SWQiKSkKICAgICAgICAgICAgbWVzc2FnZURhdGEudGVzdElkID0gaW5zdGFuY2VEYXRhLnRlc3RJZDsKICAgICAgICBwdWJzdWJUYXNrLlNldERhdGEobWVzc2FnZURhdGEpOwogICAgICAgIExvZ0luZm9ybWF0aW9uKCJEYXByUHViU3ViTWFwcGluZzogU2V0RGF0YSB0YW1hbWxhbmRpIik7CgogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpCiAgICAgICAgICAgIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImluaXRDb21wbGV0ZWQiKSkKICAgICAgICAgICAgcmVzdWx0LmluaXRDb21wbGV0ZWQgPSBkYXRhLmluaXRDb21wbGV0ZWQ7CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cyA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0YXNrUmVzdWx0cyIpKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXBySHR0cCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJIdHRwID0gZGF0YS50YXNrUmVzdWx0cy5kYXBySHR0cDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXByU2VydmljZSIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJTZXJ2aWNlID0gZGF0YS50YXNrUmVzdWx0cy5kYXByU2VydmljZTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJkYXByQmluZGluZyIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJCaW5kaW5nID0gZGF0YS50YXNrUmVzdWx0cy5kYXByQmluZGluZzsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJub3RpZmljYXRpb24iKSkKICAgICAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5ub3RpZmljYXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJ0cmlnZ2VyVHJhbnNpdGlvbiIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uID0gZGF0YS50YXNrUmVzdWx0cy50cmlnZ2VyVHJhbnNpdGlvbjsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJnZXRJbnN0YW5jZXMiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5nZXRJbnN0YW5jZXMgPSBkYXRhLnRhc2tSZXN1bHRzLmdldEluc3RhbmNlczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEudGFza1Jlc3VsdHMsICJzdWJwcm9jZXNzIikpCiAgICAgICAgICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuc3VicHJvY2VzcyA9IGRhdGEudGFza1Jlc3VsdHMuc3VicHJvY2VzczsKICAgICAgICB9CgogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByUHViU3ViID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwclB1YlN1Yi5jb21wbGV0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByUHViU3ViLmV4ZWN1dGVkQXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKCiAgICAgICAgLy8gU2tpbGwgdm5leHQtd29ya2Zsb3ctY3JlYXRpb24gwqc2LjQ6ICJjb21wbGV0ZWQgPSB0cnVlIiBsaXRlcmFsIG1hcHBpbmcgw6dhxJ9yxLFsZMSxxJ/EsW7EsQogICAgICAgIC8vIGfDtnN0ZXJpciwgdGFzaydpbiBiYcWfYXLEsWzEsSBvbGR1xJ91bnUga2FuxLF0bGFtYXouIERhcHIgUHViU3ViIHRhc2snaW5kZSBmaXJlLWFuZC1mb3JnZXQKICAgICAgICAvLyBvbGR1xJ91bmRhbiBkw7ZuZW4gdmVyaSB5b2s7IGFtYSBydW50aW1lIGNvbnRleHQuQm9keSB6YXJmxLFuZGEgdGFzayBzb251Y3UgacOnaW4KICAgICAgICAvLyBpc1N1Y2Nlc3MgKGJvb2wpIHZlIHN0YXR1c0NvZGUgKGludCkgYWxhbmxhcsSxbsSxIGTDtm5lci4gQnVubGFyxLEgcGFyZW50IGF0dHJpYnV0ZXMnYQogICAgICAgIC8vIHlhesSxcCB0ZXN0dGUgYXNzZXJ0IGVkZXJlayAidGFzayBnZXLDp2VrdGVuIHB1Ymxpc2ggZWRpbGRpIiBpZGRpYXPEsW7EsSBrYW7EsXRsxLF5b3J1ei4KICAgICAgICB2YXIgdGFza1Jlc3BvbnNlID0gY29udGV4dC5Cb2R5OwogICAgICAgIGlmICh0YXNrUmVzcG9uc2UgIT0gbnVsbCkKICAgICAgICB7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eSh0YXNrUmVzcG9uc2UsICJpc1N1Y2Nlc3MiKSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgZHluYW1pYyB0ciA9IHRhc2tSZXNwb25zZTsKICAgICAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByUHViU3ViLnB1Ymxpc2hlZCA9IENvZXJjZUJvb2wodHIuaXNTdWNjZXNzKTsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkodGFza1Jlc3BvbnNlLCAic3RhdHVzQ29kZSIpKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBkeW5hbWljIHRyID0gdGFza1Jlc3BvbnNlOwogICAgICAgICAgICAgICAgdmFyIGNvZGUgPSBDb2VyY2VJbnQzMih0ci5zdGF0dXNDb2RlKTsKICAgICAgICAgICAgICAgIGlmIChjb2RlLkhhc1ZhbHVlKQogICAgICAgICAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByUHViU3ViLnN0YXR1c0NvZGUgPSBjb2RlLlZhbHVlOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiRGFwclB1YlN1Yk1hcHBpbmcgY29tcGxldGVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBEYXByUHViU3ViTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgcHVic3ViVGFzayA9ICh0YXNrIGFzIERhcHJQdWJTdWJUYXNrKSE7CiAgICAgICAgaWYgKHB1YnN1YlRhc2sgPT0gbnVsbCkKICAgICAgICAgICAgdGhyb3cgbmV3IEludmFsaWRPcGVyYXRpb25FeGNlcHRpb24oIlRhc2sgbXVzdCBiZSBhIERhcHJQdWJTdWJUYXNrIik7CgogICAgICAgIC8vIFVzZSBEYXByUHViU3ViTmFtZSBmcm9tIFZhdWx0IHdoZW4gc2V0OyBvdGhlcndpc2Uga2VlcCB0YXNrIEpTT04gZGVmYXVsdCAodGVzdC1wdWJzdWIpLgogICAgICAgIC8vIEtlZXBzIFZhdWx0IG9wdGlvbmFsIGluIGRldjsgb3RoZXIgZW52cyAoZS5nLiB2bmV4dC1wdWJzdWIpIG9ubHkgbmVlZCB0aGUga2V5IGluIFZhdWx0LgogICAgICAgIHN0cmluZyB2YXVsdFZhbHVlID0gR2V0Q29uZmlnVmFsdWUoIkRhcHJQdWJTdWJOYW1lIik7CiAgICAgICAgaWYgKCFzdHJpbmcuSXNOdWxsT3JXaGl0ZVNwYWNlKHZhdWx0VmFsdWUpKQogICAgICAgIHsKICAgICAgICAgICAgcHVic3ViVGFzay5TZXRQdWJTdWJOYW1lKHZhdWx0VmFsdWUpOwogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkRhcHJQdWJTdWJNYXBwaW5nOiBWYXVsdCBvdmVycmlkZSBwdWJTdWJOYW1lID0ge3ZhdWx0VmFsdWV9Iik7CiAgICAgICAgfQogICAgICAgIGVsc2UKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKAogICAgICAgICAgICAgICAgJCJEYXByUHViU3ViTWFwcGluZzogRGFwclB1YlN1Yk5hbWUgbm90IGluIFZhdWx0LCB1c2luZyB0YXNrIEpTT04gZGVmYXVsdCIKICAgICAgICAgICAgKTsKICAgICAgICB9CgogICAgICAgIC8vIFB1Ymxpc2ggcmVxdWlyZXMgbm9uLW51bGwgYGRhdGFgIChBcmd1bWVudE51bGxFeGNlcHRpb24pLiBFdmVuIGlmIHRhc2sgSlNPTiBoYXMgZGF0YSwKICAgICAgICAvLyBvcmRlcmluZy9sZWdhY3kgcHVibGlzaCBpcyB1bnJlbGlhYmxlOyBlbmZvcmNlIHZpYSBtYXBwaW5nLgogICAgICAgIHZhciBpbnN0YW5jZURhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyBtZXNzYWdlRGF0YSA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgbWVzc2FnZURhdGEuZXZlbnRUeXBlID0gIkludGVncmF0aW9uVGVzdCI7CiAgICAgICAgbWVzc2FnZURhdGEuc291cmNlID0gImV4dGVuZGVkLXRhc2tzLXRlc3Qtd29ya2Zsb3ciOwogICAgICAgIG1lc3NhZ2VEYXRhLnRpbWVzdGFtcCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShpbnN0YW5jZURhdGEsICJ0ZXN0SWQiKSkKICAgICAgICAgICAgbWVzc2FnZURhdGEudGVzdElkID0gaW5zdGFuY2VEYXRhLnRlc3RJZDsKICAgICAgICBwdWJzdWJUYXNrLlNldERhdGEobWVzc2FnZURhdGEpOwogICAgICAgIExvZ0luZm9ybWF0aW9uKCJEYXByUHViU3ViTWFwcGluZzogU2V0RGF0YSBjb21wbGV0ZWQiKTsKCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkKICAgICAgICAgICAgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaW5pdENvbXBsZXRlZCIpKQogICAgICAgICAgICByZXN1bHQuaW5pdENvbXBsZXRlZCA9IGRhdGEuaW5pdENvbXBsZXRlZDsKCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRhc2tSZXN1bHRzIikpCiAgICAgICAgewogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImRhcHJIdHRwIikpCiAgICAgICAgICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwckh0dHAgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJIdHRwOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImRhcHJTZXJ2aWNlIikpCiAgICAgICAgICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwclNlcnZpY2UgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJTZXJ2aWNlOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImRhcHJCaW5kaW5nIikpCiAgICAgICAgICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwckJpbmRpbmcgPSBkYXRhLnRhc2tSZXN1bHRzLmRhcHJCaW5kaW5nOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgIm5vdGlmaWNhdGlvbiIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLm5vdGlmaWNhdGlvbiA9IGRhdGEudGFza1Jlc3VsdHMubm90aWZpY2F0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInRyaWdnZXJUcmFuc2l0aW9uIikpCiAgICAgICAgICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMudHJpZ2dlclRyYW5zaXRpb24gPSBkYXRhLnRhc2tSZXN1bHRzLnRyaWdnZXJUcmFuc2l0aW9uOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgImdldEluc3RhbmNlcyIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmdldEluc3RhbmNlcyA9IGRhdGEudGFza1Jlc3VsdHMuZ2V0SW5zdGFuY2VzOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YS50YXNrUmVzdWx0cywgInN1YnByb2Nlc3MiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5zdWJwcm9jZXNzID0gZGF0YS50YXNrUmVzdWx0cy5zdWJwcm9jZXNzOwogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC50YXNrUmVzdWx0cy5kYXByUHViU3ViLmNvbXBsZXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIuZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICAvLyBTa2lsbCB2bmV4dC13b3JrZmxvdy1jcmVhdGlvbiDCpzYuNDogImNvbXBsZXRlZCA9IHRydWUiIG9ubHkgcHJvdmVzIHRoZSBtYXBwaW5nIHJhbi4KICAgICAgICAvLyBBbGlnbiB3aXRoIGRhcHItcHVic3ViLm1kIMKnNTogcmVhZCBwdWJsaXNoZWQgLyBtZXNzYWdlSWQgb3IgZXJyb3IgZnJvbSBjb250ZXh0LkJvZHk7CiAgICAgICAgLy8gaW50ZWdyYXRpb24gdGVzdHMgYXNzZXJ0IHB1Ymxpc2hlZD09dHJ1ZS4KICAgICAgICAvLyBzdGF0dXNDb2RlIGlzIG5vdCBpbiB0aGUgZG9jOyBrZXB0IGZvciBvYnNlcnZhYmlsaXR5LgogICAgICAgIHZhciB0YXNrUmVzcG9uc2UgPSBjb250ZXh0LkJvZHk7CiAgICAgICAgaWYgKHRhc2tSZXNwb25zZSA9PSBudWxsKQogICAgICAgIHsKICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIucHVibGlzaGVkID0gZmFsc2U7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJEYXByUHViU3ViTWFwcGluZzogY29udGV4dC5Cb2R5IG51bGwsIHB1Ymxpc2hlZD1mYWxzZSIpOwogICAgICAgIH0KICAgICAgICBlbHNlIGlmIChIYXNQcm9wZXJ0eSh0YXNrUmVzcG9uc2UsICJpc1N1Y2Nlc3MiKSAmJiB0YXNrUmVzcG9uc2UuaXNTdWNjZXNzKQogICAgICAgIHsKICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIucHVibGlzaGVkID0gdHJ1ZTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KHRhc2tSZXNwb25zZSwgImRhdGEiKSAmJiB0YXNrUmVzcG9uc2UuZGF0YSAhPSBudWxsKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICB2YXIgcmVzcG9uc2VEYXRhID0gdGFza1Jlc3BvbnNlLmRhdGE7CiAgICAgICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkocmVzcG9uc2VEYXRhLCAibWVzc2FnZUlkIikpCiAgICAgICAgICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIubWVzc2FnZUlkID0gcmVzcG9uc2VEYXRhLm1lc3NhZ2VJZDsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkodGFza1Jlc3BvbnNlLCAic3RhdHVzQ29kZSIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIuc3RhdHVzQ29kZSA9IHRhc2tSZXNwb25zZS5zdGF0dXNDb2RlOwogICAgICAgIH0KICAgICAgICBlbHNlCiAgICAgICAgewogICAgICAgICAgICByZXN1bHQudGFza1Jlc3VsdHMuZGFwclB1YlN1Yi5wdWJsaXNoZWQgPSBmYWxzZTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KHRhc2tSZXNwb25zZSwgImVycm9yTWVzc2FnZSIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIuZXJyb3IgPSB0YXNrUmVzcG9uc2UuZXJyb3JNZXNzYWdlOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkodGFza1Jlc3BvbnNlLCAic3RhdHVzQ29kZSIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRhc2tSZXN1bHRzLmRhcHJQdWJTdWIuc3RhdHVzQ29kZSA9IHRhc2tSZXNwb25zZS5zdGF0dXNDb2RlOwogICAgICAgIH0KCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkRhcHJQdWJTdWJNYXBwaW5nIGNvbXBsZXRlZCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" } } ], @@ -260,10 +224,6 @@ { "language": "en-US", "label": "Auto to Completed" - }, - { - "language": "tr-TR", - "label": "Tamamlanmaya Otomatik" } ], "rule": { @@ -283,10 +243,6 @@ { "language": "en-US", "label": "Completed" - }, - { - "language": "tr-TR", - "label": "Tamamlandi" } ], "view": null, diff --git a/core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprPubSubMapping.csx b/core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprPubSubMapping.csx index c8ad5ee..70118df 100644 --- a/core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprPubSubMapping.csx +++ b/core/Workflows/task-execution/src/extended-tasks-test-workflow/DaprPubSubMapping.csx @@ -1,6 +1,5 @@ using System; using System.Dynamic; -using System.Text.Json; using System.Threading.Tasks; using BBT.Workflow.Definitions; using BBT.Workflow.Scripting; @@ -8,71 +7,14 @@ using BBT.Workflow.Scripting.Functions; public class DaprPubSubMapping : ScriptBase, IMapping { - /// - /// TaskExecutionEngine, task sonucunu JsonSerializer ile yazarken ExpandoObject içindeki - /// JsonElement referanslarını serileştiremiyor ("Operation is not valid due to the current state of the object"). - /// context.Body.isSuccess / statusCode runtime'da JsonElement olarak gelebilir — CLR tipine çeviriyoruz. - /// - private static bool CoerceBool(object o) - { - if (o == null) - return false; - if (o is bool b) - return b; - if (o is JsonElement je) - { - return je.ValueKind switch - { - JsonValueKind.True => true, - JsonValueKind.False => false, - JsonValueKind.String => bool.TryParse(je.GetString(), out var p) && p, - JsonValueKind.Number => je.TryGetInt32(out var n) ? n != 0 : false, - _ => false, - }; - } - if (o is string s) - return bool.TryParse(s, out var x) && x; - try - { - return Convert.ToBoolean(o); - } - catch - { - return false; - } - } - - private static int? CoerceInt32(object o) - { - if (o == null) - return null; - if (o is int i) - return i; - if (o is long l) - return checked((int)l); - if (o is JsonElement je && je.ValueKind == JsonValueKind.Number && je.TryGetInt32(out var n)) - return n; - if (o is string s && int.TryParse(s, out var p)) - return p; - try - { - return Convert.ToInt32(o); - } - catch - { - return null; - } - } - public Task InputHandler(WorkflowTask task, ScriptContext context) { var pubsubTask = (task as DaprPubSubTask)!; if (pubsubTask == null) throw new InvalidOperationException("Task must be a DaprPubSubTask"); - // Vault'ta DaprPubSubName varsa onu kullan, yoksa task JSON'daki default (test-pubsub) gecerli kalir. - // Boylece development'te Vault config'i opsiyonel; farkli ortamda (ornegin vnext-pubsub) - // sadece Vault'a key eklemek yeterli. + // Use DaprPubSubName from Vault when set; otherwise keep task JSON default (test-pubsub). + // Keeps Vault optional in dev; other envs (e.g. vnext-pubsub) only need the key in Vault. string vaultValue = GetConfigValue("DaprPubSubName"); if (!string.IsNullOrWhiteSpace(vaultValue)) { @@ -82,12 +24,12 @@ public class DaprPubSubMapping : ScriptBase, IMapping else { LogInformation( - $"DaprPubSubMapping: Vault'ta DaprPubSubName yok, task JSON default kullaniliyor" + $"DaprPubSubMapping: DaprPubSubName not in Vault, using task JSON default" ); } - // Publish icin `data` null olamaz (ArgumentNullException). Task JSON'da data olsa bile - // eski publish / sirasi yüzünden güven vermez; mapping ile garanti et. + // Publish requires non-null `data` (ArgumentNullException). Even if task JSON has data, + // ordering/legacy publish is unreliable; enforce via mapping. var instanceData = context.Instance.Data; dynamic messageData = new ExpandoObject(); messageData.eventType = "IntegrationTest"; @@ -96,7 +38,7 @@ public class DaprPubSubMapping : ScriptBase, IMapping if (HasProperty(instanceData, "testId")) messageData.testId = instanceData.testId; pubsubTask.SetData(messageData); - LogInformation("DaprPubSubMapping: SetData tamamlandi"); + LogInformation("DaprPubSubMapping: SetData completed"); return Task.FromResult(new ScriptResponse()); } @@ -134,26 +76,35 @@ public class DaprPubSubMapping : ScriptBase, IMapping result.taskResults.daprPubSub.completed = true; result.taskResults.daprPubSub.executedAt = DateTime.UtcNow.ToString("o"); - // Skill vnext-workflow-creation §6.4: "completed = true" literal mapping çağrıldığını - // gösterir, task'in başarılı olduğunu kanıtlamaz. Dapr PubSub task'inde fire-and-forget - // olduğundan dönen veri yok; ama runtime context.Body zarfında task sonucu için - // isSuccess (bool) ve statusCode (int) alanlarını döner. Bunları parent attributes'a - // yazıp testte assert ederek "task gerçekten publish edildi" iddiasını kanıtlıyoruz. + // Skill vnext-workflow-creation §6.4: "completed = true" only proves the mapping ran. + // Align with dapr-pubsub.md §5: read published / messageId or error from context.Body; + // integration tests assert published==true. + // statusCode is not in the doc; kept for observability. var taskResponse = context.Body; - if (taskResponse != null) + if (taskResponse == null) + { + result.taskResults.daprPubSub.published = false; + LogInformation("DaprPubSubMapping: context.Body null, published=false"); + } + else if (HasProperty(taskResponse, "isSuccess") && taskResponse.isSuccess) { - if (HasProperty(taskResponse, "isSuccess")) + result.taskResults.daprPubSub.published = true; + if (HasProperty(taskResponse, "data") && taskResponse.data != null) { - dynamic tr = taskResponse; - result.taskResults.daprPubSub.published = CoerceBool(tr.isSuccess); + var responseData = taskResponse.data; + if (HasProperty(responseData, "messageId")) + result.taskResults.daprPubSub.messageId = responseData.messageId; } if (HasProperty(taskResponse, "statusCode")) - { - dynamic tr = taskResponse; - var code = CoerceInt32(tr.statusCode); - if (code.HasValue) - result.taskResults.daprPubSub.statusCode = code.Value; - } + result.taskResults.daprPubSub.statusCode = taskResponse.statusCode; + } + else + { + result.taskResults.daprPubSub.published = false; + if (HasProperty(taskResponse, "errorMessage")) + result.taskResults.daprPubSub.error = taskResponse.errorMessage; + if (HasProperty(taskResponse, "statusCode")) + result.taskResults.daprPubSub.statusCode = taskResponse.statusCode; } LogInformation("DaprPubSubMapping completed"); diff --git a/core/Workflows/task-execution/task-execution-test-workflow.json b/core/Workflows/task-execution/task-execution-test-workflow.json index b2d12e1..f1d6de5 100644 --- a/core/Workflows/task-execution/task-execution-test-workflow.json +++ b/core/Workflows/task-execution/task-execution-test-workflow.json @@ -25,10 +25,6 @@ { "language": "en-US", "label": "Task Execution Integration Test" - }, - { - "language": "tr-TR", - "label": "Gorev Calistirma Entegrasyon Testi" } ], "functions": [], @@ -44,10 +40,6 @@ { "language": "en-US", "label": "Start Task Execution Test" - }, - { - "language": "tr-TR", - "label": "Gorev Testini Baslat" } ], "onExecutionTasks": [ @@ -76,10 +68,6 @@ { "language": "en-US", "label": "HTTP Task State" - }, - { - "language": "tr-TR", - "label": "HTTP Gorev Durumu" } ], "view": null, @@ -110,10 +98,6 @@ { "language": "en-US", "label": "Auto to Script Processing" - }, - { - "language": "tr-TR", - "label": "Script Isleme Otomatik Gecis" } ], "rule": { @@ -133,10 +117,6 @@ { "language": "en-US", "label": "Script Processing State" - }, - { - "language": "tr-TR", - "label": "Script Isleme Durumu" } ], "view": null, @@ -167,10 +147,6 @@ { "language": "en-US", "label": "Auto to Timer Wait" - }, - { - "language": "tr-TR", - "label": "Zamanlayici Beklemeye Otomatik Gecis" } ], "rule": { @@ -190,10 +166,6 @@ { "language": "en-US", "label": "Timer Wait State (Timer Task type 9)" - }, - { - "language": "tr-TR", - "label": "Zamanlayici Bekleme Durumu (Timer Task tip 9)" } ], "view": null, @@ -224,10 +196,6 @@ { "language": "en-US", "label": "Scheduled to Start Flow" - }, - { - "language": "tr-TR", - "label": "Zamanlanmis Flow Baslatma Gecisi" } ], "timer": { @@ -247,10 +215,6 @@ { "language": "en-US", "label": "Start Flow State (StartTask type 11)" - }, - { - "language": "tr-TR", - "label": "Flow Baslatma Durumu (StartTask tip 11)" } ], "view": null, @@ -281,10 +245,6 @@ { "language": "en-US", "label": "Auto to Get Instance Data" - }, - { - "language": "tr-TR", - "label": "Instance Data Alma Otomatik Gecis" } ], "rule": { @@ -304,10 +264,6 @@ { "language": "en-US", "label": "Get Instance Data State (GetInstanceDataTask type 13)" - }, - { - "language": "tr-TR", - "label": "Instance Data Alma Durumu (GetInstanceDataTask tip 13)" } ], "view": null, @@ -338,10 +294,6 @@ { "language": "en-US", "label": "Auto to Notification" - }, - { - "language": "tr-TR", - "label": "Bildirime Otomatik Gecis" } ], "rule": { @@ -361,10 +313,6 @@ { "language": "en-US", "label": "Notification Task" - }, - { - "language": "tr-TR", - "label": "Bildirim Gorevi" } ], "view": null, @@ -394,10 +342,6 @@ { "language": "en-US", "label": "Auto to Trigger Transition" - }, - { - "language": "tr-TR", - "label": "Trigger Transition'a Otomatik" } ], "rule": { @@ -417,10 +361,6 @@ { "language": "en-US", "label": "Trigger Transition Task" - }, - { - "language": "tr-TR", - "label": "Gecis Tetikleme Gorevi" } ], "view": null, @@ -451,10 +391,6 @@ { "language": "en-US", "label": "Auto to SubProcess" - }, - { - "language": "tr-TR", - "label": "SubProcess'e Otomatik" } ], "rule": { @@ -474,10 +410,6 @@ { "language": "en-US", "label": "SubProcess Task" - }, - { - "language": "tr-TR", - "label": "Alt Surec Gorevi" } ], "view": null, @@ -508,10 +440,6 @@ { "language": "en-US", "label": "Auto to GetInstances" - }, - { - "language": "tr-TR", - "label": "GetInstances'a Otomatik" } ], "rule": { @@ -531,10 +459,6 @@ { "language": "en-US", "label": "GetInstances Task" - }, - { - "language": "tr-TR", - "label": "Instance Listesi Gorevi" } ], "view": null, @@ -565,10 +489,6 @@ { "language": "en-US", "label": "Auto to Completed" - }, - { - "language": "tr-TR", - "label": "Tamamlandi Durumuna Otomatik" } ], "rule": { @@ -588,10 +508,6 @@ { "language": "en-US", "label": "Completed State" - }, - { - "language": "tr-TR", - "label": "Tamamlandi Durumu" } ], "view": null, @@ -602,4 +518,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/task-execution/task-target-workflow.json b/core/Workflows/task-execution/task-target-workflow.json index 30ab361..aa68d9d 100644 --- a/core/Workflows/task-execution/task-target-workflow.json +++ b/core/Workflows/task-execution/task-target-workflow.json @@ -16,10 +16,6 @@ { "language": "en-US", "label": "Task Execution Target Workflow" - }, - { - "language": "tr-TR", - "label": "Gorev Calistirma Hedef Is Akisi" } ], "functions": [], @@ -35,10 +31,6 @@ { "language": "en-US", "label": "Start Target" - }, - { - "language": "tr-TR", - "label": "Hedefi Baslat" } ], "onExecutionTasks": [] @@ -53,10 +45,6 @@ { "language": "en-US", "label": "Target Initial" - }, - { - "language": "tr-TR", - "label": "Hedef Baslangic" } ], "view": null, @@ -73,10 +61,6 @@ { "language": "en-US", "label": "Complete Target (manual / DirectTriggerTask)" - }, - { - "language": "tr-TR", - "label": "Hedefi Tamamla (manuel / DirectTriggerTask)" } ], "onExecutionTasks": [] @@ -92,10 +76,6 @@ { "language": "en-US", "label": "Target Completed" - }, - { - "language": "tr-TR", - "label": "Hedef Tamamlandi" } ], "view": null, @@ -106,4 +86,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/version-consistency/version-consistency-test-workflow-v2.json b/core/Workflows/version-consistency/version-consistency-test-workflow-v2.json index d496415..73e42ec 100644 --- a/core/Workflows/version-consistency/version-consistency-test-workflow-v2.json +++ b/core/Workflows/version-consistency/version-consistency-test-workflow-v2.json @@ -17,10 +17,6 @@ { "language": "en-US", "label": "Version Consistency Test (v2.0.0)" - }, - { - "language": "tr-TR", - "label": "Versiyon Tutarliligi Testi (v2.0.0)" } ], "functions": [], @@ -36,10 +32,6 @@ { "language": "en-US", "label": "Start Version Test" - }, - { - "language": "tr-TR", - "label": "Versiyon Testini Baslat" } ], "onExecutionTasks": [ @@ -68,10 +60,6 @@ { "language": "en-US", "label": "Init State" - }, - { - "language": "tr-TR", - "label": "Baslangic Durumu" } ], "view": null, @@ -88,10 +76,6 @@ { "language": "en-US", "label": "Auto to Processing" - }, - { - "language": "tr-TR", - "label": "Isleme Otomatik Gecis" } ], "rule": { @@ -111,10 +95,6 @@ { "language": "en-US", "label": "Processing State" - }, - { - "language": "tr-TR", - "label": "Isleme Durumu" } ], "view": null, @@ -131,10 +111,6 @@ { "language": "en-US", "label": "Complete Processing" - }, - { - "language": "tr-TR", - "label": "Islemi Tamamla" } ], "onExecutionTasks": [] @@ -150,10 +126,6 @@ { "language": "en-US", "label": "Review State (v2 only)" - }, - { - "language": "tr-TR", - "label": "Inceleme Durumu (sadece v2)" } ], "view": null, @@ -184,10 +156,6 @@ { "language": "en-US", "label": "Approve Review" - }, - { - "language": "tr-TR", - "label": "Incelemeyi Onayla" } ], "onExecutionTasks": [] @@ -203,10 +171,6 @@ { "language": "en-US", "label": "Completed (v2)" - }, - { - "language": "tr-TR", - "label": "Tamamlandi (v2)" } ], "view": null, @@ -231,4 +195,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/version-consistency/version-consistency-test-workflow.json b/core/Workflows/version-consistency/version-consistency-test-workflow.json index 020f72b..02ec351 100644 --- a/core/Workflows/version-consistency/version-consistency-test-workflow.json +++ b/core/Workflows/version-consistency/version-consistency-test-workflow.json @@ -16,10 +16,6 @@ { "language": "en-US", "label": "Version Consistency Test (v1.0.0)" - }, - { - "language": "tr-TR", - "label": "Versiyon Tutarliligi Testi (v1.0.0)" } ], "functions": [], @@ -35,10 +31,6 @@ { "language": "en-US", "label": "Start Version Test" - }, - { - "language": "tr-TR", - "label": "Versiyon Testini Baslat" } ], "onExecutionTasks": [ @@ -67,10 +59,6 @@ { "language": "en-US", "label": "Init State" - }, - { - "language": "tr-TR", - "label": "Baslangic Durumu" } ], "view": null, @@ -87,10 +75,6 @@ { "language": "en-US", "label": "Auto to Processing" - }, - { - "language": "tr-TR", - "label": "Isleme Otomatik Gecis" } ], "rule": { @@ -110,10 +94,6 @@ { "language": "en-US", "label": "Processing State" - }, - { - "language": "tr-TR", - "label": "Isleme Durumu" } ], "view": null, @@ -130,10 +110,6 @@ { "language": "en-US", "label": "Complete Processing" - }, - { - "language": "tr-TR", - "label": "Islemi Tamamla" } ], "onExecutionTasks": [] @@ -149,10 +125,6 @@ { "language": "en-US", "label": "Completed (v1)" - }, - { - "language": "tr-TR", - "label": "Tamamlandi (v1)" } ], "view": null, @@ -177,4 +149,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/view-function-extension/view-function-extension-test-workflow.json b/core/Workflows/view-function-extension/view-function-extension-test-workflow.json index ee0280a..86211ef 100644 --- a/core/Workflows/view-function-extension/view-function-extension-test-workflow.json +++ b/core/Workflows/view-function-extension/view-function-extension-test-workflow.json @@ -28,10 +28,6 @@ { "language": "en-US", "label": "View, Function and Extension Integration Test" - }, - { - "language": "tr-TR", - "label": "Gorunum Fonksiyon ve Uzantı Entegrasyon Testi" } ], "functions": [ @@ -80,10 +76,6 @@ { "language": "en-US", "label": "Start View and Function Extension Test" - }, - { - "language": "tr-TR", - "label": "Gorunum Fonksiyon Uzantı Testini Baslat" } ], "onExecutionTasks": [ @@ -112,10 +104,6 @@ { "language": "en-US", "label": "JSON View State" - }, - { - "language": "tr-TR", - "label": "JSON Gorunum Durumu" } ], "view": { @@ -139,10 +127,6 @@ { "language": "en-US", "label": "Auto to Multi View (WebPlatformRule)" - }, - { - "language": "tr-TR", - "label": "Coklu Gorunume Otomatik Gecis (WebPlatformRule)" } ], "rule": { @@ -161,10 +145,6 @@ { "language": "en-US", "label": "Default Auto Fallback (AlwaysTrueRule)" - }, - { - "language": "tr-TR", - "label": "Varsayilan Otomatik Geri Donus (AlwaysTrueRule)" } ], "rule": { @@ -184,10 +164,6 @@ { "language": "en-US", "label": "HTML View State" - }, - { - "language": "tr-TR", - "label": "HTML Gorunum Durumu" } ], "view": { @@ -211,10 +187,6 @@ { "language": "en-US", "label": "Continue to Wizard State" - }, - { - "language": "tr-TR", - "label": "Sihirbaz Durumuna Devam" } ], "schema": null, @@ -233,10 +205,6 @@ { "language": "en-US", "label": "Wizard State" - }, - { - "language": "tr-TR", - "label": "Sihirbaz Durumu" } ], "view": null, @@ -253,10 +221,6 @@ { "language": "en-US", "label": "Complete with Markdown View" - }, - { - "language": "tr-TR", - "label": "Markdown Gorunumu ile Tamamla" } ], "schema": null, @@ -282,10 +246,6 @@ { "language": "en-US", "label": "Completed State" - }, - { - "language": "tr-TR", - "label": "Tamamlandi Durumu" } ], "view": null, @@ -296,4 +256,4 @@ } ] } -} \ No newline at end of file +} diff --git a/doc/integration-test-documentation.md b/doc/integration-test-documentation.md index 582af30..dd753e3 100644 --- a/doc/integration-test-documentation.md +++ b/doc/integration-test-documentation.md @@ -45,7 +45,7 @@ core/ **Amac:** Workflow yasam dongusu ve tum transition tiplerini test eder; ayrica **exit transition**, **zamanlayici schedule yonetimi** (manuel iptal ve **self-loop reschedule** — hedef olarak mevcut state anahtari `auto-passed-state`; `$self` ile ayni davranis), **workflow/state queryRoles** ve **`GET .../functions/state` icinde transition listesinin role gore filtrelenmesi** senaryolarini kapsar. -**Onemli (transition `roles`):** Runtime modelinde transition uzerindeki `roles` (allow/deny), **PATCH ile gecisi calistirmayi engelleyen bir yetki kapisi degildir**; agirlikli olarak **state fonksiyonu yanitindaki `transitions[]` listesini** cagiran rol header'ina gore **suzmek** icin kullanilir. Rolü izin listesinde olmayan bir istemci, UI/API listesinde o gecisi **gormeyebilir**; bu, dokumante edilen davranista gecisin API ile **mutlaka reddedilecegi** anlamina gelmez. (Ayrı mekanizma: **`authorize?queryRoles=...`** ile workflow/instance datasi sorgu yetkisi.) +**Important (transition `roles`):** In the runtime model, transition `roles` (allow/deny) are **not a hard PATCH gate that blocks invoking the transition**; they primarily **filter the `transitions[]` list in the state-function response** for the caller role header. A client not on the allow list may **omit** that transition from UI/API lists; documented behaviour **does not** mean the transition is **always rejected** via the API. (**Separate mechanism:** **`authorize?queryRoles=...`** for querying workflow/instance data.) **C# testleri:** `StateFunction_*` testleri, ilgili **role header** ile state sorgulandiginda `transitions[]` icinde beklenen anahtarin **listelenip listelenmedigini** dogrular. @@ -95,7 +95,7 @@ lifecycle-transitions-test-workflow (F) │ ├── cancel-schedule-manually (Manual, triggerType:0) → pre-complete-state │ │ └── (zamanlayici schedule'ini manuel transition ile iptal senaryosu) │ └── reschedule-timer (Manual, triggerType:0, target: auto-passed-state) -│ └── (aynı state'te self-loop; timer'i yeniden zamanlama — tanim dogrulamasinda `$self` yerine acik state anahtari gerekir) +│ └── (same-state self-loop; reschedule timer — use explicit state key rather than `$self` in definition validation) │ ├── timer-triggered-state (Intermediate, stateType:2) │ ├── onEntries: @@ -143,7 +143,7 @@ lifecycle-transitions-test-workflow (F) | **Timer reschedule (self-loop)** | Ayni state'e manuel gecis ile zamanlayici reschedule | reschedule-timer → **auto-passed-state**; sonra yeniden kurulan `scheduled-timer-transition` (~6s) → timer-triggered → auto → pre-complete | | **queryRoles (workflow)** | authorize endpoint ile workflow datasi sorgu yetkisi | attributes.queryRoles, test-viewer | | **queryRoles (state)** | State bazli allow/deny override | **pre-complete-state** queryRoles (`processing-state` auto gecisli; testte cancel-schedule ile pre-complete) | -| **Transition `roles`** | **`GET .../functions/state` yanıtında `transitions[]` listesini** role gore **filtreleme** (v0.0.39+); transition yürütme (PATCH) için aynı listedeki deny'in zorunlu red anlamı **yoktur** | move-to-processing, complete-workflow; xUnit: `StateFunction_WithTestOperatorRole_*` / `StateFunction_WithTestViewerRole_*` | +| **Transition `roles`** | **Filter `transitions[]` in `GET .../functions/state`** by role (v0.0.39+); **no** guaranteed PATCH denial solely from deny on that list | move-to-processing, complete-workflow; xUnit: `StateFunction_WithTestOperatorRole_*` / `StateFunction_WithTestViewerRole_*` | | Version strategy (Major/Minor/Patch) | Farkli versiyonlama stratejileri | Cesitli transitions | | Idempotent start | Ayni key ile tekrar baslatma | HTTP test dosyasinda | @@ -325,7 +325,7 @@ subflow-orchestration-parent (F) **Workflow'lar:** - `task-execution-test-workflow` (type: F) — Ana test workflow'u (HTTP, Script, Timer Wait, StartFlow, GetInstanceData, Notification, TriggerTransition, SubProcess, GetInstances → completed). Human Task (tip 5) runtime tarafindan kaldirilacak gecici bir ozellik oldugu icin bu zincirden cikartildi. - `task-target-workflow` (type: F) — StartFlow / GetInstanceData / TriggerTransition / SubProcess / GetInstances icin hedef -- `extended-tasks-test-workflow` (type: F) — **Sadece Dapr** ile dis servis cagrisi: HTTP invoke, Service, PubSub (tek zincir; HTTP test: `api-tests/task-execution/task-execution.http` **Bolum 3**). **Not:** Dapr Binding (tip 2) test kapsamindan cikarildi (bkz. dosya sonu **Test Edilmeyen Ozellikler** bolumu). +- `extended-tasks-test-workflow` (type: F) — **Dapr-only** outbound calls: HTTP invoke, Service, PubSub (single chain; HTTP test: `api-tests/task-execution/task-execution.http` **Section 3**). **Note:** Dapr Binding (type 2) was removed from test scope (see **Untested Features** at end of file). **Amac:** Tum **runtime-ici** gorev tipleri ile **Dapr tabanli** gorev tiplerini ayri workflow'larda dogrular; task siralama (`onEntries` order), `context.Body.data`, scheduled transition (Timer Task tip 9), auto transition (Condition Task tip 8) ve Dapr bilesen YAML (`etc/dapr/components/`) ile uyumu kapsar. @@ -450,9 +450,9 @@ extended-tasks-test-workflow (F) | CSX | `extended-tasks-test-workflow` mapping'leri | `Workflows/task-execution/src/extended-tasks-test-workflow/*.csx` | | CSX | Ortak auto-transition rule | `Workflows/task-execution/src/shared/AlwaysTrueRule.csx` | -**HTTP / Postman:** `api-tests/task-execution/task-execution.http` (Bolum 1-3, happy path). Postman: `postman-task-execution-collection.json` (aynı üç bölüm). +**HTTP / Postman:** `api-tests/task-execution/task-execution.http` (Sections 1–3, happy path). Postman: `postman-task-execution-collection.json` (same three sections). -**C# integration testler:** `tests/Core/Tests/task-execution-test-workflow/` (`TaskExecutionTestWorkflowTests`, `TaskExecutionInstanceDataAssertions`) — B1/B2/B3 happy path, ana workflow için instance data sözleşmesi ve extended Dapr `taskResults` bayrakları. +**C# integration tests:** `tests/Core/Tests/task-execution-test-workflow/` (`TaskExecutionTestWorkflowTests`, `TaskExecutionInstanceDataAssertions`) — B1/B2/B3 happy paths, instance data contract on the main workflow, and extended Dapr `taskResults` flags. **Workflow:** `error-boundary-test-workflow` (type: F) @@ -671,7 +671,7 @@ view-function-extension-test-workflow (F) **Amac:** Master schema, transition schema validasyonu, updateData ($self) mekanizmasi ve field roles ozelliklerini test eder. -**Not (field roles vs. transition roles):** Bu gruptaki **field roles**, master şema uzerinde **alan bazlı gorunurluk** (or. `GET .../functions/data` ciktisinda rol ile alan suzme) ile ilgilidir. **Grup 1** `lifecycle-transitions` workflow'undaki transition **`roles`** alani ise **`GET .../functions/state` icindeki `transitions[]` listeleme filtresi** icindir; ikisi farkli mekanizmalardir. +**Note (field roles vs. transition roles):** Here **field roles** are **field visibility** on the master schema (e.g. role-based field filtering in `GET .../functions/data`). The transition **`roles`** field on **`lifecycle-transitions`** in **Group 1** is the **`transitions[]` listing filter for `GET .../functions/state`**; the two mechanisms differ. ### Agac Yapisi @@ -723,7 +723,7 @@ schema-data-test-workflow (F) | Field roles (master schema alan gorunurlugu) | Alan bazli data gorunurlugu | internalNote (admin/customer), auditLog (auditor) | | JSON Schema Draft 2020-12 | Schema standardi uyumu | Her iki schema dosyasi | | Required alan kontrolu | Zorunlu alanlarin validasyonu | orderId, customerName, amount, currency | -| Enum validasyonu | Gecerli deger kümesi kontrolu | currency: TRY, USD, EUR | +| Enum validation | Allowed value-set check | currency: TRY, USD, EUR | | ETag | Instance data versiyonlama (HTTP test) | HTTP test dosyasinda | ### Kullanilan Bilesenler @@ -1039,7 +1039,7 @@ Integration testlerde kullanilan C# script interface'leri: |-------|----------| | `HasProperty(obj, "propName")` | Dynamic nesnede property var mi kontrol eder | | `LogInformation($"mesaj")` | Loglama yapar | -| `GetConfigValue("Key")` | Vault'tan konfigürasyon degeri okur | +| `GetConfigValue("Key")` | Reads configuration value from Vault | --- diff --git a/postman/vNext Example Runtime.postman_collection.json b/postman/vNext Example Runtime.postman_collection.json index bf2f645..63aa068 100644 --- a/postman/vNext Example Runtime.postman_collection.json +++ b/postman/vNext Example Runtime.postman_collection.json @@ -504,7 +504,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"key\": \"349874994574\",\n \"tags\": [\n \"test\",\n \"oauth\",\n \"authentication\"\n ],\n \"attributes\": {\n \"userId\": 1,\n \"amount\": 12000,\n \"currency\": \"TL\",\n \"frequency\": \"monthly\",\n \"startDate\": \"2025-10-01T09:02:38.201Z\",\n \"endDate\": \"2026-10-01T09:02:38.201Z\",\n \"paymentMethodId\": \"1\",\n \"description\": \"Tayfun ödeme\",\n \"recipientId\": \"324324\",\n \"isAutoRetry\": false,\n \"maxRetries\": 3\n }\n}", + "raw": "{\n \"key\": \"349874994574\",\n \"tags\": [\n \"test\",\n \"oauth\",\n \"authentication\"\n ],\n \"attributes\": {\n \"userId\": 1,\n \"amount\": 12000,\n \"currency\": \"TL\",\n \"frequency\": \"monthly\",\n \"startDate\": \"2025-10-01T09:02:38.201Z\",\n \"endDate\": \"2026-10-01T09:02:38.201Z\",\n \"paymentMethodId\": \"1\",\n \"description\": \"Tayfun payment\",\n \"recipientId\": \"324324\",\n \"isAutoRetry\": false,\n \"maxRetries\": 3\n }\n}", "options": { "raw": { "language": "json" @@ -2021,7 +2021,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"attributes\": {\n \"approvedBy\": \"user-123\",\n \"approvalNotes\": \"Döküman incelendi ve onaylandı\"\n }\n}", + "raw": "{\n \"attributes\": {\n \"approvedBy\": \"user-123\",\n \"approvalNotes\": \"Document reviewed and approved\"\n }\n}", "options": { "raw": { "language": "json" @@ -2077,7 +2077,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"attributes\": {\n \"rejectedBy\": \"user-123\",\n \"reason\": \"Döküman içeriğinde eksiklik tespit edildi\"\n }\n}", + "raw": "{\n \"attributes\": {\n \"rejectedBy\": \"user-123\",\n \"reason\": \"Missing information in document content\"\n }\n}", "options": { "raw": { "language": "json" diff --git a/tests/Core/Helpers/InstanceListJson.cs b/tests/Core/Helpers/InstanceListJson.cs new file mode 100644 index 0000000..cd59b7d --- /dev/null +++ b/tests/Core/Helpers/InstanceListJson.cs @@ -0,0 +1,107 @@ +using System.Text.Json; + +namespace Core.IntegrationTests.Helpers; + +/// +/// Parses GET .../workflows/{workflow}/instances list responses. +/// Standard runtime envelope (see vnext-runtime/README.tr.md, "Response Formati"): +/// +/// { +/// "data": [ { "id": "...", "key": "...", "flow": "...", "currentState": "...", "status": "...", "attributes": { ... } } ], +/// "pagination": { "page": N, "pageSize": N, "totalCount": N, "totalPages": N } +/// } +/// +/// Legacy/alternate wrappers are also handled: root array or items. +/// +public static class InstanceListJson +{ + /// + /// Returns instance rows from a list response as []. + /// Returns empty list when none found. + /// + public static IReadOnlyList ExtractItems(JsonElement body) + { + if (body.ValueKind == JsonValueKind.Array) + return body.EnumerateArray().ToList(); + + if (body.ValueKind != JsonValueKind.Object) + return Array.Empty(); + + if (body.TryGetProperty("data", out var data) && data.ValueKind == JsonValueKind.Array) + return data.EnumerateArray().ToList(); + + if (body.TryGetProperty("items", out var items) && items.ValueKind == JsonValueKind.Array) + return items.EnumerateArray().ToList(); + + return Array.Empty(); + } + + /// + /// Reads from the pagination object + /// (e.g. page, pageSize, totalCount, totalPages) as int; otherwise null. + /// + public static int? TryGetPaginationInt(JsonElement body, string propertyName) + { + if ( + body.ValueKind == JsonValueKind.Object + && body.TryGetProperty("pagination", out var pag) + && pag.ValueKind == JsonValueKind.Object + && pag.TryGetProperty(propertyName, out var el) + && el.ValueKind == JsonValueKind.Number + && el.TryGetInt32(out var value) + ) + return value; + + return null; + } + + /// + /// Whether the list contains a row whose id equals (case-sensitive). + /// + public static bool ContainsInstanceId(JsonElement body, string instanceId) + { + if (string.IsNullOrEmpty(instanceId)) + return false; + + foreach (var item in ExtractItems(body)) + { + if (item.ValueKind != JsonValueKind.Object) + continue; + if ( + item.TryGetProperty("id", out var idEl) + && idEl.ValueKind == JsonValueKind.String + && string.Equals(idEl.GetString(), instanceId, StringComparison.Ordinal) + ) + return true; + } + return false; + } + + /// + /// Returns attributes object from an instance row, or null. + /// + public static JsonElement? TryGetAttributes(JsonElement instance) + { + if ( + instance.ValueKind == JsonValueKind.Object + && instance.TryGetProperty("attributes", out var attrs) + && attrs.ValueKind == JsonValueKind.Object + ) + return attrs; + return null; + } + + /// + /// Returns id from an instance row, or null. + /// + public static string? TryGetId(JsonElement instance) + { + if ( + instance.ValueKind == JsonValueKind.Object + && instance.TryGetProperty("id", out var idEl) + && idEl.ValueKind == JsonValueKind.String + ) + return idEl.GetString(); + return null; + } +} diff --git a/tests/Core/Helpers/JsonElementAssertions.cs b/tests/Core/Helpers/JsonElementAssertions.cs index cd32739..fb0b65e 100644 --- a/tests/Core/Helpers/JsonElementAssertions.cs +++ b/tests/Core/Helpers/JsonElementAssertions.cs @@ -4,7 +4,7 @@ namespace Core.IntegrationTests.Helpers; /// -/// üzerinde sık kullanılan test assertion'ları (ör. GetInstance attributes). +/// Frequently used test assertions over (e.g. GetInstance attributes). /// public static class JsonElementAssertions { @@ -93,9 +93,9 @@ public static void AssertNestedPropertyTrue( } /// - /// Nested object yolundaki string property'nin non-empty oldugunu dogrular. - /// Skill vnext-workflow-creation §6.4: task'in gercekten calistigi, yaniti parent attributes'a - /// non-empty deger olarak yansidiginda kanitlanir (sabit literal "completed = true" yetersiz). + /// Verifies that the nested string property is non-empty. + /// Skill vnext-workflow-creation §6.4: that the task actually ran is proven when the response + /// surfaces as a non-empty value on parent attributes (a literal "completed": true alone is insufficient). /// public static void AssertNestedPropertyNonEmptyString( JsonElement root, diff --git a/tests/Core/Helpers/StateFunctionJson.cs b/tests/Core/Helpers/StateFunctionJson.cs index 857977d..df9873e 100644 --- a/tests/Core/Helpers/StateFunctionJson.cs +++ b/tests/Core/Helpers/StateFunctionJson.cs @@ -4,7 +4,7 @@ namespace Core.IntegrationTests.Helpers; /// /// Parses GET .../functions/state JSON for tests. Shape may evolve; extend with new branches if needed. -/// State cevabında geçişler genelde transitions[].name ile listelenir; bkz. . +/// Transitions are usually listed as transitions[].name; see . /// public static class StateFunctionJson { diff --git a/tests/Core/Tests/instance-management-test-workflow/InstanceManagementInstanceDataAssertions.cs b/tests/Core/Tests/instance-management-test-workflow/InstanceManagementInstanceDataAssertions.cs new file mode 100644 index 0000000..4cd040f --- /dev/null +++ b/tests/Core/Tests/instance-management-test-workflow/InstanceManagementInstanceDataAssertions.cs @@ -0,0 +1,80 @@ +using System.Globalization; +using System.Text.Json; +using Core.IntegrationTests.Helpers; +using Xunit; + +namespace Core.IntegrationTests.Tests.InstanceManagementTestWorkflow; + +/// +/// Workflow-specific assertions for instance attributes on instance-management-test-workflow. +/// InitInstanceMgmtMapping.csx under startTransition.onExecutionTasks establishes: +/// +/// category: string (default "default") +/// priority: int (default 1) +/// testStarted: true +/// startedAt: ISO-8601 UTC string (parsable with DateTimeStyles.RoundtripKind) +/// +/// Per vnext-tests-as-code layout rules, assertions specific to one workflow live in that scenario folder; +/// shared helpers stay in and . +/// +public static class InstanceManagementInstanceDataAssertions +{ + /// + /// Validates baseline fields produced by init mapping. If and + /// are omitted, only presence and contract checks apply. + /// + public static void AssertInitialAttributes( + JsonElement attributes, + string? expectedCategory = null, + int? expectedPriority = null + ) + { + JsonElementAssertions.AssertPropertyTrue( + attributes, + "testStarted", + "InitInstanceMgmtMapping should set 'testStarted = true'." + ); + + JsonElementAssertions.AssertPropertyNonEmptyString( + attributes, + "startedAt", + "InitInstanceMgmtMapping should set 'startedAt' as ISO-8601 UTC string." + ); + + var startedAtRaw = attributes.GetProperty("startedAt").GetString()!; + var parsed = DateTime.Parse( + startedAtRaw, + CultureInfo.InvariantCulture, + DateTimeStyles.RoundtripKind + ); + Assert.True( + parsed.Kind == DateTimeKind.Utc, + $"'startedAt' should be UTC (Kind=Utc); got Kind={parsed.Kind}, raw='{startedAtRaw}'." + ); + + JsonElementAssertions.AssertPropertyNonEmptyString( + attributes, + "category", + "InitInstanceMgmtMapping should set 'category' (falls back to \"default\")." + ); + if (expectedCategory is not null) + { + JsonElementAssertions.AssertPropertyString( + attributes, + "category", + expectedCategory, + $"'category' should equal '{expectedCategory}' (propagated from start attributes)." + ); + } + + Assert.True( + attributes.TryGetProperty("priority", out var priorityEl) + && priorityEl.ValueKind == JsonValueKind.Number, + "InitInstanceMgmtMapping should set 'priority' as a number (falls back to 1)." + ); + if (expectedPriority is not null) + { + Assert.Equal(expectedPriority.Value, priorityEl.GetInt32()); + } + } +} diff --git a/tests/Core/Tests/instance-management-test-workflow/InstanceManagementTestWorkflowTests.cs b/tests/Core/Tests/instance-management-test-workflow/InstanceManagementTestWorkflowTests.cs new file mode 100644 index 0000000..b121969 --- /dev/null +++ b/tests/Core/Tests/instance-management-test-workflow/InstanceManagementTestWorkflowTests.cs @@ -0,0 +1,490 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text.Json; +using System.Threading.Tasks; +using Core.IntegrationTests.Helpers; +using Core.IntegrationTests.Infrastructure; +using Xunit; + +namespace Core.IntegrationTests.Tests.InstanceManagementTestWorkflow; + +/// +/// Integration tests for core/Workflows/instance-management/instance-management-test-workflow.json. +/// Coverage aligns with the doc/integration-test-documentation.md "Group 7: Instance Management" matrix +/// and steps in api-tests/instance-management/postman-instance-management.json. +/// +/// Features under test: +/// +/// Manual transition variants (process/fast-complete/finish/reject/suspend/set-busy/assign-human) +/// Final state subType 1 / 2 / 4 / 5 / 6 (completed / rejected / suspended / busy / human) +/// Workflow timeout (PT120S → timeout-state subType 3) +/// Idempotent start (two POSTs with the same key return the same instance id) +/// Instance list filtering — GraphQL / JSON shape (attributes, currentState, status) +/// Pagination (page / pageSize); list sort by createdAt asc/desc without filter where sort is isolated +/// InitInstanceMgmtMapping contract (category / priority / testStarted / startedAt) +/// +/// +/// Notes: +/// - The filter parameter is always in GraphQL / JSON form (vnext-workflow-creation §7). +/// - The SDK applies HttpClient URL encoding via ListInstancesAsync query-parameter dictionary; +/// when the filter JSON string is passed as a dictionary value, {/}/" are encoded automatically. +/// +public class InstanceManagementTestWorkflowTests : IntegrationTestBase +{ + private const string WorkflowKey = "instance-management-test-workflow"; + + /// Tolerance window for short manual-transition flows. + private static readonly TimeSpan ShortStateTimeout = TimeSpan.FromSeconds(30); + + /// + /// Workflow timeout is PT120S (2 minutes). Conservative upper bound ~3 minutes for slow CI. + /// (No lower bound; PollStateUntilAnyAsync does not short-circuit the “did the timer actually wait?” case.) + /// + private static readonly TimeSpan WorkflowTimeoutWait = TimeSpan.FromMinutes(3); + + private readonly WorkflowInstanceTestHelper _wf; + + public InstanceManagementTestWorkflowTests(VNextTestEnvironment environment) + : base(environment) + { + _wf = new WorkflowInstanceTestHelper(Api, WorkflowKey); + } + + // ----------------------------------------------------------------------- + // Transition variants + final state subTypes + // ----------------------------------------------------------------------- + + [Fact] + public async Task HappyPath_ProcessThenFinish_ReachesCompletedState() + { + var instanceId = await StartDefaultInstanceAsync("finance", 1); + await _wf.WaitForStateAsync(instanceId, "active-state", ShortStateTimeout); + + await _wf.RunTransitionAsync(instanceId, "process", headers: null); + await _wf.WaitForStateAsync(instanceId, "processing-state", ShortStateTimeout); + + await _wf.RunTransitionAsync(instanceId, "finish", headers: null); + await _wf.WaitForStateAsync(instanceId, "completed-state", ShortStateTimeout); + + // completed-state subType:1 → happy-path final: status "C" (Completed). + var body = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("C", StateFunctionJson.ExtractStatus(body)); + + var attrs = await GetAttributesAsync(WorkflowKey, instanceId); + InstanceManagementInstanceDataAssertions.AssertInitialAttributes( + attrs, + expectedCategory: "finance", + expectedPriority: 1 + ); + } + + [Fact] + public async Task FastComplete_FromActive_ReachesCompletedStateWithoutProcessing() + { + var instanceId = await StartDefaultInstanceAsync(category: null, priority: null); + + await _wf.WaitForStateAsync(instanceId, "active-state", ShortStateTimeout); + await _wf.RunTransitionAsync(instanceId, "fast-complete", headers: null); + await _wf.WaitForStateAsync(instanceId, "completed-state", ShortStateTimeout); + + var body = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("C", StateFunctionJson.ExtractStatus(body)); + + // When category/priority are omitted from the start body, init mapping applies defaults. + var attrs = await GetAttributesAsync(WorkflowKey, instanceId); + InstanceManagementInstanceDataAssertions.AssertInitialAttributes( + attrs, + expectedCategory: "default", + expectedPriority: 1 + ); + } + + [Fact] + public async Task RejectPath_ReachesRejectedState_SubType2_StillCompleted() + { + var instanceId = await StartDefaultInstanceAsync(category: "risky", priority: 3); + + await _wf.WaitForStateAsync(instanceId, "active-state", ShortStateTimeout); + await _wf.RunTransitionAsync(instanceId, "process", headers: null); + await _wf.WaitForStateAsync(instanceId, "processing-state", ShortStateTimeout); + await _wf.RunTransitionAsync(instanceId, "reject", headers: null); + await _wf.WaitForStateAsync(instanceId, "rejected-state", ShortStateTimeout); + + // rejected-state is stateType:3 (final) + subType:2; instance reached a final state so status is "C". + // Note: status is NOT "F" — "F" means faulted/exception; here reject is a normal terminal state, so runtime marks Completed (C). + var body = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("C", StateFunctionJson.ExtractStatus(body)); + } + + [Fact] + public async Task SuspendPath_ReachesSuspendedState_SubType4() + { + var instanceId = await StartDefaultInstanceAsync(category: "ops", priority: 2); + await RunThreeStepTransitionAsync(instanceId, "suspend", "suspended-state"); + + // subType:4 (Temporarily Suspended) — runtime state subType filtering treats this final state as Suspended (see instance-filtering.md "Instance Subtype (4,5,6) Filtering"). + } + + [Fact] + public async Task BusyPath_ReachesBusyState_SubType5() + { + var instanceId = await StartDefaultInstanceAsync(category: "ops", priority: 2); + await RunThreeStepTransitionAsync(instanceId, "set-busy", "busy-state"); + } + + [Fact] + public async Task HumanPath_ReachesHumanState_SubType6() + { + var instanceId = await StartDefaultInstanceAsync(category: "ops", priority: 2); + await RunThreeStepTransitionAsync(instanceId, "assign-human", "human-state"); + } + + // ----------------------------------------------------------------------- + // Idempotent start (repeat POST with same key → same instance id) + // ----------------------------------------------------------------------- + + [Fact] + public async Task IdempotentStart_SameKey_ReturnsSameInstanceId() + { + var uniqueKey = WorkflowInstanceTestHelper.UniqueInstanceKey("idempotent"); + var body = new + { + key = uniqueKey, + tags = new[] { "integration-test", "instance-management", "idempotent" }, + attributes = new { category = "idempotent-test", priority = 1 }, + }; + + var firstId = await _wf.StartInstanceIdAsync(body); + var secondId = await _wf.StartInstanceIdAsync(body); + + Assert.Equal(firstId, secondId); + } + + // ----------------------------------------------------------------------- + // Filtering (GraphQL / JSON) — vnext-workflow-creation §7 + // ----------------------------------------------------------------------- + + [Fact] + public async Task Filter_ByCategoryFinance_ListContainsOnlyFinanceInstances() + { + var financeId = await StartAndAdvanceToActiveAsync(category: "finance", priority: 1); + var opsId = await StartAndAdvanceToActiveAsync(category: "ops", priority: 2); + + // filter={"attributes":{"category":{"eq":"finance"}}} + var body = await ListInstancesWithFilterAsync( + "{\"attributes\":{\"category\":{\"eq\":\"finance\"}}}" + ); + + Assert.True( + InstanceListJson.ContainsInstanceId(body, financeId), + $"finance instance ({financeId}) should be present in category=finance filter result." + ); + Assert.False( + InstanceListJson.ContainsInstanceId(body, opsId), + $"ops instance ({opsId}) should NOT be present in category=finance filter result." + ); + + // Verify every listed instance has attributes.category == "finance". + foreach (var item in InstanceListJson.ExtractItems(body)) + { + var attrs = InstanceListJson.TryGetAttributes(item); + if (attrs is null) + continue; + if (attrs.Value.TryGetProperty("category", out var catEl)) + { + Assert.Equal("finance", catEl.GetString()); + } + } + } + + [Fact] + public async Task Filter_ByCurrentState_ActiveState_ReturnsInstancesInActiveState() + { + var activeId = await StartAndAdvanceToActiveAsync(category: "finance", priority: 1); + var completedId = await StartAndAdvanceToActiveAsync(category: "ops", priority: 1); + await _wf.RunTransitionAsync(completedId, "fast-complete", headers: null); + await _wf.WaitForStateAsync(completedId, "completed-state", ShortStateTimeout); + + // filter={"currentState":{"eq":"active-state"}} + var body = await ListInstancesWithFilterAsync( + "{\"currentState\":{\"eq\":\"active-state\"}}" + ); + + Assert.True( + InstanceListJson.ContainsInstanceId(body, activeId), + $"active instance ({activeId}) should be listed by currentState=active-state filter." + ); + Assert.False( + InstanceListJson.ContainsInstanceId(body, completedId), + $"completed instance ({completedId}) should NOT be listed by currentState=active-state filter." + ); + } + + [Fact] + public async Task Filter_ByStatus_ActiveCode_ReturnsOnlyActiveInstances() + { + var activeId = await StartAndAdvanceToActiveAsync(category: "finance", priority: 1); + var completedId = await StartAndAdvanceToActiveAsync(category: "ops", priority: 1); + await _wf.RunTransitionAsync(completedId, "fast-complete", headers: null); + await _wf.WaitForStateAsync(completedId, "completed-state", ShortStateTimeout); + + // filter={"status":{"eq":"A"}} — status code "A" (Active). + var body = await ListInstancesWithFilterAsync("{\"status\":{\"eq\":\"A\"}}"); + + Assert.True( + InstanceListJson.ContainsInstanceId(body, activeId), + $"active instance ({activeId}) should be listed by status=A filter." + ); + Assert.False( + InstanceListJson.ContainsInstanceId(body, completedId), + $"completed instance ({completedId}) should NOT appear under status=A filter." + ); + } + + // ----------------------------------------------------------------------- + // Sorting by createdAt (asc/desc) and pagination + // ----------------------------------------------------------------------- + + [Fact] + public async Task Sort_DescendingByCreatedAt_NewestInstanceAppearsBeforeOldest() + { + var firstId = await StartAndAdvanceToActiveAsync(category: "finance", priority: 1); + // Short delay so createdAt ordering is distinguishable (precision may be sub-second). + await Task.Delay(1200); + var secondId = await StartAndAdvanceToActiveAsync(category: "finance", priority: 1); + + // Sort only (no filter) — isolates list sort behaviour. Large pageSize so both instances + // are likely on the first page in typical integration environments. + var queryParams = new Dictionary + { + ["sort"] = "-createdAt", + ["pageSize"] = "100", + }; + var response = await Api.ListInstancesAsync(WorkflowKey, queryParams); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + var firstIdx = IndexOfInstance(response.Body, firstId); + var secondIdx = IndexOfInstance(response.Body, secondId); + + Assert.True(firstIdx >= 0, $"first instance ({firstId}) should be listed."); + Assert.True(secondIdx >= 0, $"second instance ({secondId}) should be listed."); + Assert.True( + secondIdx < firstIdx, + "Newest instance (second) should appear before the older one under sort=-createdAt; " + + $"secondIdx={secondIdx}, firstIdx={firstIdx}." + ); + } + + [Fact] + public async Task Sort_AscendingByCreatedAt_OldestInstanceAppearsBeforeNewest() + { + var firstId = await StartAndAdvanceToActiveAsync(category: "finance", priority: 1); + await Task.Delay(1200); + var secondId = await StartAndAdvanceToActiveAsync(category: "finance", priority: 1); + + var queryParams = new Dictionary + { + // Ascending creation time — same shorthand family as `-createdAt` / GetInstances doc (`FieldName`). + ["sort"] = "createdAt", + ["pageSize"] = "100", + }; + var response = await Api.ListInstancesAsync(WorkflowKey, queryParams); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + var firstIdx = IndexOfInstance(response.Body, firstId); + var secondIdx = IndexOfInstance(response.Body, secondId); + + Assert.True(firstIdx >= 0, $"first instance ({firstId}) should be listed."); + Assert.True(secondIdx >= 0, $"second instance ({secondId}) should be listed."); + Assert.True( + firstIdx < secondIdx, + "Older instance (first) should appear before the newer one under ascending createdAt sort; " + + $"firstIdx={firstIdx}, secondIdx={secondIdx}." + ); + } + + [Fact] + public async Task Pagination_WithPageSize_LimitsPageItemsAndReportsTotalCount() + { + // Create at least three distinct finance instances. + var ids = new List + { + await StartAndAdvanceToActiveAsync(category: "finance", priority: 1), + await StartAndAdvanceToActiveAsync(category: "finance", priority: 1), + await StartAndAdvanceToActiveAsync(category: "finance", priority: 1), + }; + + var queryParams = new Dictionary + { + ["filter"] = "{\"attributes\":{\"category\":{\"eq\":\"finance\"}}}", + ["page"] = "1", + ["pageSize"] = "2", + ["sort"] = "-createdAt", + }; + var response = await Api.ListInstancesAsync(WorkflowKey, queryParams); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + var items = InstanceListJson.ExtractItems(response.Body); + Assert.True( + items.Count <= 2, + $"page=1&pageSize=2 should cap returned items at 2; got {items.Count}." + ); + + // If a pagination object exists, page/pageSize should be reported correctly. + var page = InstanceListJson.TryGetPaginationInt(response.Body, "page"); + var pageSize = InstanceListJson.TryGetPaginationInt(response.Body, "pageSize"); + var totalCount = InstanceListJson.TryGetPaginationInt(response.Body, "totalCount"); + if (page is not null) + Assert.Equal(1, page); + if (pageSize is not null) + Assert.Equal(2, pageSize); + if (totalCount is not null) + Assert.True( + totalCount >= ids.Count, + $"pagination.totalCount ({totalCount}) should be >= created finance count ({ids.Count})." + ); + } + + // ----------------------------------------------------------------------- + // Workflow timeout (PT120S → timeout-state, subType 3) + // ----------------------------------------------------------------------- + + /// + /// Workflow-level timeout moves instances still in active-state after 120 seconds automatically to + /// timeout-state (stateType:3, subType:3). The test polls with a short period; upper bound ~3 minutes for CI. + /// + [Fact] + public async Task WorkflowTimeout_AfterPT120S_InstanceAutoMovesToTimeoutState() + { + var instanceId = await StartAndAdvanceToActiveAsync(category: "timeout-test", priority: 1); + + // If we leave before 120s the assertion is meaningless; tolerate up to ~3 min poll. + await _wf.WaitForStateAsync(instanceId, "timeout-state", WorkflowTimeoutWait); + + var body = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + // timeout-state is final so status "C" (Completed) is expected + // (F = Faulted; no exception here — runtime treats timeout as normal terminal). + Assert.Equal("C", StateFunctionJson.ExtractStatus(body)); + } + + // ----------------------------------------------------------------------- + // Private helpers + // ----------------------------------------------------------------------- + + /// + /// category and priority are optional; when null the start body omits those fields + /// and init mapping writes defaults ("default" / 1). + /// + private Task StartDefaultInstanceAsync(string? category, int? priority) => + _wf.StartInstanceIdAsync( + BuildStartBody(category, priority, tagSuffix: "default") + ); + + /// + /// Start an instance and wait until it reaches active-state. + /// List/filter tests need a well-defined source state per instance. + /// + private async Task StartAndAdvanceToActiveAsync(string? category, int? priority) + { + var instanceId = await _wf.StartInstanceIdAsync( + BuildStartBody(category, priority, tagSuffix: "list-filter") + ); + await _wf.WaitForStateAsync(instanceId, "active-state", ShortStateTimeout); + return instanceId; + } + + private static object BuildStartBody(string? category, int? priority, string tagSuffix) + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey($"instance-mgmt-{tagSuffix}"); + var tags = new[] { "integration-test", "instance-management", tagSuffix }; + + if (category is null && priority is null) + return new + { + key, + tags, + }; + + if (category is null) + return new + { + key, + tags, + attributes = new { priority = priority!.Value }, + }; + + if (priority is null) + return new + { + key, + tags, + attributes = new { category }, + }; + + return new + { + key, + tags, + attributes = new { category, priority = priority.Value }, + }; + } + + private async Task RunThreeStepTransitionAsync( + string instanceId, + string processingTransition, + string expectedFinalState + ) + { + await _wf.WaitForStateAsync(instanceId, "active-state", ShortStateTimeout); + await _wf.RunTransitionAsync(instanceId, "process", headers: null); + await _wf.WaitForStateAsync(instanceId, "processing-state", ShortStateTimeout); + await _wf.RunTransitionAsync(instanceId, processingTransition, headers: null); + await _wf.WaitForStateAsync(instanceId, expectedFinalState, ShortStateTimeout); + + var body = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + // On reaching a final state, status should be "C" (happy-path terminal). + Assert.Equal("C", StateFunctionJson.ExtractStatus(body)); + } + + private async Task ListInstancesWithFilterAsync(string filterJson) + { + var queryParams = new Dictionary + { + ["filter"] = filterJson, + // Large page size in test env — avoid coupling with pagination tests. + ["pageSize"] = "100", + ["sort"] = "-createdAt", + }; + var response = await Api.ListInstancesAsync(WorkflowKey, queryParams); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + return response.Body; + } + + private static int IndexOfInstance(JsonElement body, string instanceId) + { + var items = InstanceListJson.ExtractItems(body); + for (int i = 0; i < items.Count; i++) + { + if ( + string.Equals( + InstanceListJson.TryGetId(items[i]), + instanceId, + StringComparison.Ordinal + ) + ) + return i; + } + return -1; + } + + private async Task GetAttributesAsync(string workflowKey, string instanceId) + { + var response = await Api.GetInstanceAsync(workflowKey, instanceId); + Assert.True( + response.Body.TryGetProperty("attributes", out var attributes), + "GetInstance response should include 'attributes'." + ); + return attributes; + } +} diff --git a/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecyclePassPathInstanceDataAssertions.cs b/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecyclePassPathInstanceDataAssertions.cs index a5e666e..86b636c 100644 --- a/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecyclePassPathInstanceDataAssertions.cs +++ b/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecyclePassPathInstanceDataAssertions.cs @@ -6,8 +6,8 @@ namespace Core.IntegrationTests.Tests.LifecycleTransitionsTestWorkflow; /// -/// lifecycle-transitions-test-workflow pass path (timer ile pre-complete-state) için instance -/// attributes sözleşmesi. Alan adları core/Workflows/lifecycle-transitions/src/*.csx ile hizalıdır. +/// Instance attributes contract for lifecycle-transitions-test-workflow pass path (timer to pre-complete-state). +/// Field names align with core/Workflows/lifecycle-transitions/src/*.csx. /// internal static class LifecyclePassPathInstanceDataAssertions { diff --git a/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs b/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs index c89e4d0..e7001ed 100644 --- a/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs +++ b/tests/Core/Tests/lifecycle-transitions-test-workflow/LifecycleTransitionsTestWorkflowTests.cs @@ -5,15 +5,15 @@ namespace Core.IntegrationTests.Tests.LifecycleTransitionsTestWorkflow; /* -! Buglar: +! Bugs: ! Cancel ! Exit -! Reschedule (reschedule-timer sonrasi scheduled transition yeniden kurulmuyor; instance auto-passed-state'te kaliyor) +! Reschedule (after reschedule-timer the scheduled transition is not re-armed; instance stays on auto-passed-state) */ /// /// Integration tests aligned with api-tests/lifecycle-transitions/lifecycle-transitions-test-workflow.http. -/// Paylaşılan API yardımcıları: ; bu workflow’a özel veri sözleşmesi: +/// Shared API helpers: ; workflow-specific data contract: /// . /// public class LifecycleTransitionsTestWorkflowTests : IntegrationTestBase @@ -67,12 +67,11 @@ await _workflow.RunTransitionAsync( } /// - /// Timer ile pre-complete-state'e ulaşan pass path'te instance verisinde, ilgili state'lerde tanımlı - /// onEntry / onExit (ve geçiş) script görevlerinin instance data'ya yazdığı alanların beklenen şekilde - /// dolduğunu doğrular. Böylece bu görevlerin fiilen çalıştığı veri sözleşmesi üzerinden garanti altına alınır. + /// On the pass path that reaches pre-complete-state via timer, verifies instance data fields written by + /// onEntry / onExit (and transition) script tasks match expectations — contract-level proof tasks ran. /// /// - /// Alan adları core/Workflows/lifecycle-transitions/src/*.csx ile eşleşir; script gövdeleri değişirse bu test bilinçli olarak güncellenmelidir. + /// Field names match core/Workflows/lifecycle-transitions/src/*.csx; update this test deliberately if scripts change. /// [Fact] public async Task PassPath_InstanceData_AfterPreCompleteViaTimer_ReflectsOnEntryOnExitScripts() @@ -181,7 +180,7 @@ public async Task IdempotentStart_SameKey_ReturnsSameInstanceId() Assert.Equal(id1, id2); } - // TODO: Bu test şu anda başarısız — runtime/platform tarafında cancel ile ilgili bug var; workflow tanımı doğru kabul ediliyor. + // TODO: Currently failing — cancel bug on runtime/platform side; workflow definition assumed correct. [Fact] public async Task CancelTransition_ReachesTerminatedState() { @@ -200,7 +199,7 @@ public async Task CancelTransition_ReachesTerminatedState() await _workflow.AssertStateAsync(instanceId, "terminated-state"); } - // TODO: Bu test şu anda başarısız — runtime/platform tarafında exit ile ilgili bug var; workflow tanımı doğru kabul ediliyor. + // TODO: Currently failing — exit bug on runtime/platform side; workflow definition assumed correct. [Fact] public async Task ExitTransition_FromInitialize_SetsExitExecutedOnInstance() { @@ -233,7 +232,7 @@ public async Task ExitTransition_FromInitialize_SetsExitExecutedOnInstance() ); } - // TODO: Bu test şu anda başarısız — runtime/platform tarafında exit ile ilgili bug var; workflow tanımı doğru kabul ediliyor. + // TODO: Currently failing — exit bug on runtime/platform side; workflow definition assumed correct. [Fact] public async Task ExitTransition_FromPreComplete_SetsExitExecutedOnInstance() { @@ -304,12 +303,12 @@ await _workflow.RunTransitionAsync( await _workflow.AssertStateAsync(instanceId, "pre-complete-state"); } - // TODO / BUG: reschedule-timer çalışınca workflow tanımına göre scheduled-timer-transition yeniden kurulmalı (~6 sonra timer-triggered → pre-complete). - // Şu an runtime’da timer yeniden schedule olmuyor; instance sürekli auto-passed-state’te kalıyor — WaitForStateAsync(pre-complete) zaman aşımına düşer. - // Workflow JSON / reschedule-timer → auto-passed-state ($self) doğru kabul ediliyor; platform tarafı incelenmeli. + // TODO / BUG: After reschedule-timer the definition expects scheduled-timer-transition to be re-armed (~6s then timer-triggered → pre-complete). + // Runtime does not reschedule; instance stays on auto-passed-state — WaitForStateAsync(pre-complete) times out. + // Workflow JSON reschedule-timer → auto-passed-state ($self) assumed correct; platform needs investigation. /// - /// reschedule-timer: $self ile hâlâ auto-passed-state; ardından zamanlayıcı yeniden kurulmalı (ShortTimerMapping +6). - /// Beklenen: scheduled-timer-transition -> timer-triggered-state -> auto-to-pre-complete -> pre-complete-state. + /// reschedule-timer: stays on auto-passed-state via $self; timer should reschedule (ShortTimerMapping +6s). + /// Expected: scheduled-timer-transition -> timer-triggered-state -> auto-to-pre-complete -> pre-complete-state. /// [Fact] public async Task RescheduleTimer_SelfTransition_ThenWaitsForRescheduledTimer_ReachesPreCompleteState() @@ -336,7 +335,7 @@ await _workflow.RunTransitionAsync( await _workflow.AssertStateAsync(instanceId, "auto-passed-state"); - // Yeniden kurulan timer ~6 sn; timer-triggered onEntries + auto gecis icin pay + // Rescheduled timer ~6s; slack for timer-triggered onEntries + auto transition await _workflow.WaitForStateAsync( instanceId, "pre-complete-state", diff --git a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs index 94819c0..5db6401 100644 --- a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs +++ b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionInstanceDataAssertions.cs @@ -5,8 +5,8 @@ namespace Core.IntegrationTests.Tests.TaskExecutionTestWorkflow; /// -/// Instance attributes sözleşmesi: api-tests/task-execution/task-execution.http B1/B3 yorumları ile -/// core/Workflows/task-execution/src/.../*.csx hizalıdır. +/// Instance attributes contract aligned with api-tests/task-execution/task-execution.http B1/B3 comments and +/// core/Workflows/task-execution/src/.../*.csx. /// internal static class TaskExecutionMainWorkflowInstanceDataAssertions { @@ -14,15 +14,12 @@ internal static class TaskExecutionMainWorkflowInstanceDataAssertions "(task-execution-test-workflow mappings + task-execution.http B1 expected flags)"; /// - /// Happy path tamamlandiginda: HTTP, script, timer-wait (Timer Task tip 9), start-flow, - /// get-instance-data, trigger, subprocess ve get-instances onEntry mapping bayraklari. - /// Notification task mapping.type: G kullandigi icin taskResults.notification - /// entegrasyon assert'ine dahil edilmez. Human Task (tip 5) runtime tarafindan kaldirilacak - /// gecici bir ozellik oldugu icin bu workflow'da kullanilmaz; happy path human onayi olmadan - /// dogrudan completed-state'e ulasir. timer-wait-state, scheduled transition - /// (triggerType: 2) + ITimerMapping ile 3 sn bekledigi icin - /// timerStartedAt attribute'u zorunludur ve test bunun uzerinden gercek bekleme - /// suresini dogrular. + /// On happy-path completion: HTTP, script, timer-wait (Timer type 9), start-flow, + /// get-instance-data, trigger, subprocess, get-instances onEntry mapping flags. + /// Notification task uses mapping.type: G so taskResults.notification is not asserted. + /// Human Task (type 5) is deprecated; not used — path reaches completed-state without human approval. + /// timer-wait-state uses scheduled transition (triggerType: 2) + ITimerMapping ~3s wait; + /// timerStartedAt is required and tests use it to verify real delay. /// public static void AssertHappyPathCompleted(JsonElement attributes) { @@ -77,8 +74,7 @@ public static void AssertHappyPathCompleted(JsonElement attributes) "completed", ContractHint ); - // SubProcessTask (tip 14) gercekten yeni bir hedef instance acmali; SubProcessMapping - // OutputHandler'i runtime yanitindaki id alanini parent attributes.subprocessInstanceId'ye yazar. + // SubProcessTask must start a new target instance; SubProcessMapping maps runtime id to parent subprocessInstanceId. JsonElementAssertions.AssertPropertyNonEmptyString( attributes, "subprocessInstanceId", @@ -100,22 +96,20 @@ public static void AssertHappyPathCompleted(JsonElement attributes) } /// -/// extended-tasks-test-workflow sonunda Dapr zinciri için attributes kontrolleri. -/// Dapr Binding (tip 2) state ve task'i workflow'dan kaldirildi (bkz. integration-test-documentation.md -/// "Test Edilmeyen Ozellikler" bolumu); zincir su an HTTP -> Service -> PubSub seklinde ilerler. +/// attributes checks for extended-tasks-test-workflow Dapr chain at completion. +/// Dapr Binding (type 2) state/task removed per integration-test-documentation "Untested Features"; +/// chain is HTTP -> Service -> PubSub. /// -/// Skill vnext-workflow-creation §6.4 geregi her Dapr task'i icin sabit "completed = true" literal -/// bayraginin yaninda, task'in gercekten calistigini kanitlayan ek bir alan da assert edilir: -/// - daprHttp / daprService: mocklab yanitindan gelen processId (GUID) parent attributes'a -/// yazilir; non-empty string olmasi mocklab'a gercekten ulasildiginin kanitidir. -/// - daprPubSub: PubSub fire-and-forget oldugundan response body'sinde alan donmez; runtime -/// context.Body.isSuccess bayragini doner ve mapping bunu published olarak -/// parent attributes'a yazar; published == true task'in basarili publish ettiginin kanitidir. +/// Per vnext-workflow-creation §6.4, each Dapr task asserts both literal completed = true and an +/// observable proof field: +/// - daprHttp / daprService: processId (GUID) from mocklab proves the call landed. +/// - daprPubSub: fire-and-forget returns no payload; runtime exposes context.Body.isSuccess; mapping writes +/// published on parent attributes; published == true proves successful publish. /// internal static class ExtendedTasksWorkflowInstanceDataAssertions { private const string ContractHint = - "(extended-tasks-test-workflow Dapr mappings + task-execution.http B3 yorumları)"; + "(extended-tasks-test-workflow Dapr mappings + task-execution.http B3 comments)"; public static void AssertDaprChainCompleted(JsonElement attributes) { @@ -127,13 +121,12 @@ public static void AssertDaprChainCompleted(JsonElement attributes) "completed", ContractHint ); - // Skill §6.4: mapping bayragi "completed=true" literal; gercek task yanitinin (mocklab GUID) - // parent attributes'a yansidigi processId ile kanitlanir. + // Skill §6.4: completed=true literal + mocklab GUID on parent proves real response handling. JsonElementAssertions.AssertNestedPropertyNonEmptyString( attributes, new[] { "taskResults", "daprHttp" }, "processId", - $"daprHttp.processId mocklab yanitindan gelmeli {ContractHint}" + $"daprHttp.processId should come from mocklab response {ContractHint}" ); JsonElementAssertions.AssertNestedPropertyTrue( @@ -146,7 +139,7 @@ public static void AssertDaprChainCompleted(JsonElement attributes) attributes, new[] { "taskResults", "daprService" }, "processId", - $"daprService.processId mocklab yanitindan gelmeli {ContractHint}" + $"daprService.processId should come from mocklab response {ContractHint}" ); JsonElementAssertions.AssertNestedPropertyTrue( @@ -155,13 +148,12 @@ public static void AssertDaprChainCompleted(JsonElement attributes) "completed", ContractHint ); - // PubSub fire-and-forget: runtime context.Body.isSuccess'i mapping parent attributes'a - // "published" olarak yazar; true olmasi PubSub broker'a gercekten gonderildigin kanitidir. + // Fire-and-forget: runtime maps context.Body.isSuccess to parent "published". JsonElementAssertions.AssertNestedPropertyTrue( attributes, new[] { "taskResults", "daprPubSub" }, "published", - $"daprPubSub.published runtime isSuccess bayragindan gelmeli {ContractHint}" + $"daprPubSub.published should reflect runtime isSuccess {ContractHint}" ); } } diff --git a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs index 5d7d317..1ce346a 100644 --- a/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs +++ b/tests/Core/Tests/task-execution-test-workflow/TaskExecutionTestWorkflowTests.cs @@ -7,17 +7,16 @@ namespace Core.IntegrationTests.Tests.TaskExecutionTestWorkflow; /* !BUGS: -! 1. Dapr Pubsub'un context body'sinden dönen JSONELEMENT -! yapının json serialize işleminde hata veriyor, -! dispose olmuş / uygun olmayan durumdaki JsonElement +! 1. JsonElement from Dapr PubSub context.Body fails on JSON serialization: +! InvalidOperationException due to disposed or invalid object state. * System.InvalidOperationException: Operation is not valid due to the current state of the object. * at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.GetMemberAndWriteJson(...) * at BBT.Workflow.Tasks.Coordinator.TaskExecutionEngine.ExecuteCoreAsync(...):line 531 */ /// -/// api-tests/task-execution/task-execution.http B1–B3 ve -/// doc/integration-test-documentation.md Task Execution grubu ile hizalı integration testler. +/// Integration tests aligned with api-tests/task-execution/task-execution.http B1–B3 and +/// the Task Execution section of doc/integration-test-documentation.md. /// public class TaskExecutionTestWorkflowTests : IntegrationTestBase { @@ -25,7 +24,7 @@ public class TaskExecutionTestWorkflowTests : IntegrationTestBase private const string TargetWorkflowKey = "task-target-workflow"; private const string ExtendedWorkflowKey = "extended-tasks-test-workflow"; - /// Ana zincir: Mocklab HTTP, subprocess, harici servisler — toleranslı bekleme. + /// Main chain: Mocklab HTTP, subprocess, external services — tolerant timeouts. private static readonly TimeSpan MainWorkflowStateTimeout = TimeSpan.FromMinutes(3); private static readonly TimeSpan ExtendedDaprTimeout = TimeSpan.FromMinutes(3); @@ -55,10 +54,9 @@ public async Task B1_MainWorkflow_HappyPath_ReachesCompleted() } ); - // Workflow zinciri: http -> script -> timer-wait (3 sn) -> start-flow -> get-instance-data + // Flow: http -> script -> timer-wait (3s) -> start-flow -> get-instance-data // -> notification -> trigger-transition -> subprocess -> get-instances -> completed-state. - // Human Task (tip 5) runtime tarafindan kaldirilacak gecici bir ozellik oldugu icin bu - // workflow'da kullanilmaz; happy path manuel onay olmadan dogrudan completed-state'e ulasir. + // Human Task (type 5) is deprecated in runtime; not used — happy path completes without manual approval. await _mainWorkflow.WaitForStateAsync( instanceId, "completed-state", @@ -70,17 +68,16 @@ await _mainWorkflow.WaitForStateAsync( headers: null ); var status = StateFunctionJson.ExtractStatus(stateCompleted); - // Happy path bittiginde instance tamamlanmistir; GET .../functions/state govdesindeki status 'C' (Completed) olmalidir. + // On happy-path completion, GET .../functions/state must report status 'C' (Completed). Assert.Equal("C", status); var attrs = await GetAttributesAsync(MainWorkflowKey, instanceId); TaskExecutionMainWorkflowInstanceDataAssertions.AssertHappyPathCompleted(attrs); - // Timer Task (tip 9) gercekten beklemis mi? timer-wait-state'in scheduled transition'i - // ITimerMapping ile 3 sn sonra start-flow-state'e gecirir; eger sadece auto transition - // calissaydi completed-state'e ~yari saniyede ulasirdik. timerStartedAt parent attributes'unda - // yazildigi icin (TimerStartMapping) completed-state'e ulasilan ana kadar gecen sure 3 sn'den - // buyuk olmalidir. Toleransi 2.5 sn olarak aliyoruz; ust sinir koymuyoruz (CI yavasligi). + // Did Timer Task (type 9) actually wait? timer-wait-state scheduled transition + ITimerMapping + // advances to start-flow-state after ~3s; auto-only would reach completed-state in ~0.5s. + // timerStartedAt on parent attributes (TimerStartMapping) — elapsed time to completed-state should exceed 3s. + // Use 2.5s tolerance; no upper bound (slow CI). var timerStartedAtRaw = attrs.GetProperty("timerStartedAt").GetString()!; var timerStartedAt = DateTime.Parse( timerStartedAtRaw, @@ -90,14 +87,13 @@ await _mainWorkflow.WaitForStateAsync( var timerElapsed = DateTime.UtcNow - timerStartedAt; Assert.True( timerElapsed.TotalSeconds >= 2.5, - $"Timer Task (tip 9) should have delayed scheduled transition by ~3s; " + $"Timer Task (type 9) should delay scheduled transition by ~3s; " + $"elapsed since timerStartedAt={timerStartedAtRaw} is only {timerElapsed.TotalSeconds:F2}s. " - + "Eger bu deger 3 sn'den kucukse scheduled transition timer'i devre disi kalmis olabilir." + + "If below 3s, the scheduled transition timer may be disabled." ); - // StartFlow + DirectTrigger zincirinin sadece parent instance'a `startedInstanceId` yazmis olmasi yeterli degildir. - // Hedef workflow'un `functions/state` cagrisiyla; (1) StartTask yeni bir instance acti, (2) DirectTriggerTask - // hedefin manuel `manual-complete-target` gecisini gercekten tetikledi, dogrulanir. + // StartFlow + DirectTrigger: parent `startedInstanceId` alone is insufficient. + // Confirm via target `functions/state`: (1) StartTask opened new instance, (2) DirectTriggerTask fired manual `manual-complete-target`. var startedTargetId = attrs.GetProperty("startedInstanceId").GetString(); Assert.False( string.IsNullOrWhiteSpace(startedTargetId), @@ -114,11 +110,10 @@ await _mainWorkflow.WaitForStateAsync( ); Assert.Equal("C", StateFunctionJson.ExtractStatus(targetStateAfterTrigger)); - // SubProcessTask (tip 14) ayri bir hedef instance acar (fire-and-forget). - // attributes.subprocessInstanceId, SubProcessMapping OutputHandler'inda runtime yanitindan yazilir; - // varligi ve hedef workflow GET functions/state cevabinin Active/Completed olmasi, subprocess'in - // gercekten baslatildigini kanitlar (sadece "completed" bayragi yetmez). Ayrica GetInstance ile - // hedef instance attributes'inda parentInstanceId/source/note alanlarinin yazildigini dogrulariz. + // SubProcessTask (type 14) starts a separate target instance (fire-and-forget). + // attributes.subprocessInstanceId from SubProcessMapping OutputHandler; presence + target GET functions/state + // Active/Completed proves startup (a "completed" flag alone is not enough). GetInstance checks + // parentInstanceId/source/note on the child. var subprocessInstanceId = attrs.GetProperty("subprocessInstanceId").GetString(); Assert.False( string.IsNullOrWhiteSpace(subprocessInstanceId), @@ -129,9 +124,7 @@ await _mainWorkflow.WaitForStateAsync( subprocessInstanceId!, headers: null ); - // Subprocess fire-and-forget oldugu icin tetiklemiyoruz; ya target-initial'da Active kalmali - // ya da runtime tarafindan otomatik Completed olmus olmali. Her iki durumda da instance'in - // gercekten yaratildigi GET 200 + gecerli state ile teyit edilir. + // Fire-and-forget — do not drive transitions; remain target-initial or auto-completed. var subprocessState = StateFunctionJson.ExtractStateName(subprocessStateBody); var subprocessStatus = StateFunctionJson.ExtractStatus(subprocessStateBody); Assert.True( @@ -162,17 +155,13 @@ await _mainWorkflow.WaitForStateAsync( "subprocess instance attributes.note (set via SubProcessTask body)" ); - // SubProcess (tip 14) baslatildiginda parent instance'in `functions/state` cevabindaki - // `activeCorrelations` listesinde subprocess'e karsilik gelen bir correlation bulunur: - // - `subFlowInstanceId` parent attributes'taki `subprocessInstanceId` ile birebir esit, - // - `subFlowType` SubProcess kisa kodu olan "P". - // Bu, parent'in attributes'ina yazilan id'nin (mapping uretimi) yaninda runtime'in da - // sub-flow correlation'ini gercekten kaydettigini kanitlar; sadece "completed = true" - // bayragi yetmez (vnext-runtime/doc/tr/flow/function.md "Sub-flow Korelasyonlari" tablosu; - // vnext-tests-as-code skill "ActiveCorrelations ile SubProcess / SubFlow teyidi" bolumu). - // Not: Eger parent COMPLETED iken activeCorrelations bos donerse bu adim fail olur ve - // doğrulamayi parent subprocess'i baslattiktan hemen sonraki bir state snapshot'inda - // yapmaya tasimak gerekir; ilk fail durumunda hata mesaji bu durumu da belirtir. + // On SubProcess start, parent `functions/state` exposes `activeCorrelations`: + // - subFlowInstanceId matches parent attributes subprocessInstanceId, + // - subFlowType is SubProcess short code "P". + // Proves runtime correlation, not only mapping-produced id (vnext-runtime function.md sub-flow table; + // vnext-tests-as-code ActiveCorrelations section). + // If parent is COMPLETED and activeCorrelations is empty this fails — move assertion to an earlier snapshot + // taken while parent is still in subprocess-related state (error message mentions this). var correlationFound = StateFunctionJson.TryFindActiveCorrelationBySubFlowInstanceId( stateCompleted, subprocessInstanceId!, @@ -181,16 +170,16 @@ out var subprocessCorrelation var allCorrelations = StateFunctionJson.ExtractActiveCorrelations(stateCompleted); Assert.True( correlationFound, - $"parent functions/state.activeCorrelations icinde subFlowInstanceId == '{subprocessInstanceId}' olan correlation bulunmali; " - + $"toplam correlation sayisi = {allCorrelations.Count}. " - + "Eger 0 geldiyse parent COMPLETED'a ulastiginda runtime correlation'i listeden cikarmis olabilir; " - + "doğrulamayi parent subprocess-state'inde iken alinan bir snapshot'a tasimak gerekebilir." + $"parent functions/state.activeCorrelations must contain subFlowInstanceId == '{subprocessInstanceId}'; " + + $"total correlations = {allCorrelations.Count}. " + + "If zero, runtime may strip correlations once parent reaches COMPLETED; " + + "then assert from a snapshot taken while parent is still in subprocess-state." ); var subFlowType = StateFunctionJson.ExtractSubFlowType(subprocessCorrelation); Assert.True( string.Equals(subFlowType, "P", StringComparison.Ordinal), - $"activeCorrelations[].subFlowType beklenen 'P' (SubProcess kisa kodu); actual = '{subFlowType ?? ""}'." + $"activeCorrelations[].subFlowType expected 'P' (SubProcess); actual = '{subFlowType ?? ""}'." ); } @@ -231,7 +220,7 @@ await _targetWorkflow.WaitForStateAsync( headers: null ); var targetStatus = StateFunctionJson.ExtractStatus(targetStateBody); - // Hedef workflow happy path sonunda state fonksiyonu status 'C' (Completed) olmalıdır. + // Target workflow happy path: state function status must be 'C' (Completed). Assert.Equal("C", targetStatus); } @@ -259,7 +248,7 @@ await _extendedWorkflow.WaitForStateAsync( headers: null ); var extendedStatus = StateFunctionJson.ExtractStatus(extendedStateBody); - // Dapr zinciri happy path sonunda state fonksiyonu status 'C' (Completed) olmalıdır. + // Dapr chain happy path: state function status must be 'C' (Completed). Assert.Equal("C", extendedStatus); var attrs = await GetAttributesAsync(ExtendedWorkflowKey, instanceId); From f456557462e850c46a8b827902e27f5db57d1fca Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Mon, 4 May 2026 15:05:37 +0300 Subject: [PATCH 13/24] instance management sorting test bug fixed. flow type tests are added. --- api-tests/flow-types/flow-types-test.http | 38 +++ core.link.json | 2 +- core/Workflows/flow-types/flow-flow-test.json | 122 +++++++ .../flow-types/src/FlowInitMapping.csx | 25 ++ .../flow-types/src/SubFlowInitMapping.csx | 25 ++ .../flow-types/subflow-flow-test.json | 122 +++++++ .../instance-management-test-workflow.json | 2 +- doc/integration-test-documentation.md | 44 ++- tests/Core/Helpers/InstanceListJson.cs | 24 ++ .../Helpers/WorkflowInstanceTestHelper.cs | 50 +++ tests/Core/Tests/flow-types/FlowTypesTests.cs | 78 +++++ .../InstanceManagementScenarioActions.cs | 109 +++++++ .../InstanceManagementTestWorkflowTests.cs | 299 ++++++------------ vnext-example.sln.DotSettings.user | 4 + 14 files changed, 743 insertions(+), 201 deletions(-) create mode 100644 core/Workflows/flow-types/flow-flow-test.json create mode 100644 core/Workflows/flow-types/src/FlowInitMapping.csx create mode 100644 core/Workflows/flow-types/src/SubFlowInitMapping.csx create mode 100644 core/Workflows/flow-types/subflow-flow-test.json create mode 100644 tests/Core/Tests/flow-types/FlowTypesTests.cs create mode 100644 tests/Core/Tests/instance-management-test-workflow/InstanceManagementScenarioActions.cs create mode 100644 vnext-example.sln.DotSettings.user diff --git a/api-tests/flow-types/flow-types-test.http b/api-tests/flow-types/flow-types-test.http index d357eec..1617db6 100644 --- a/api-tests/flow-types/flow-types-test.http +++ b/api-tests/flow-types/flow-types-test.http @@ -41,3 +41,41 @@ Content-Type: application/json ### Adim 4: SubProcess State Kontrol GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/subprocess-flow-test/instances/{{subProcessInstanceId}}/functions/state + +### ===== FLOW TYPE (F) TEST ===== + +### Adim 5: Flow (F) Baslat +# @name startFlow +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/flow-flow-test/instances/start +Content-Type: application/json + +{ + "key": "flow-test-{{$timestamp}}", + "tags": ["test", "flow-type"], + "attributes": {} +} + +### Flow Instance ID yakala +@flowInstanceId = {{startFlow.response.body.$.id}} + +### Adim 6: Flow State Kontrol (auto transition ile flow-completed'a gecmis olmali) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/flow-flow-test/instances/{{flowInstanceId}}/functions/state + +### ===== SUBFLOW TYPE (S) TEST ===== + +### Adim 7: SubFlow (S) Baslat +# @name startSubFlow +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/subflow-flow-test/instances/start +Content-Type: application/json + +{ + "key": "subflow-test-{{$timestamp}}", + "tags": ["test", "subflow-type"], + "attributes": {} +} + +### SubFlow Instance ID yakala +@subFlowInstanceId = {{startSubFlow.response.body.$.id}} + +### Adim 8: SubFlow State Kontrol (auto transition ile subflow-completed'a gecmis olmali) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/subflow-flow-test/instances/{{subFlowInstanceId}}/functions/state diff --git a/core.link.json b/core.link.json index 3566c5d..1b6eb5e 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-05-04T10:35:23.871Z" + "importedAt": "2026-05-04T11:35:58.531Z" } \ No newline at end of file diff --git a/core/Workflows/flow-types/flow-flow-test.json b/core/Workflows/flow-types/flow-flow-test.json new file mode 100644 index 0000000..044b72d --- /dev/null +++ b/core/Workflows/flow-types/flow-flow-test.json @@ -0,0 +1,122 @@ +{ + "key": "flow-flow-test", + "flow": "sys-flows", + "flowVersion": "1.0.0", + "domain": "core", + "version": "1.0.0", + "tags": [ + "integration-test", + "flow-types", + "flow" + ], + "attributes": { + "type": "F", + "timeout": null, + "errorBoundary": null, + "labels": [ + { + "language": "en-US", + "label": "Flow Type (F) Integration Test" + } + ], + "functions": [], + "features": [], + "extensions": [], + "sharedTransitions": [], + "startTransition": { + "key": "start-flow-flow-test", + "target": "flow-init-state", + "triggerType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Start Flow Type Test" + } + ], + "onExecutionTasks": [ + { + "order": 1, + "task": { + "key": "flow-types-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/FlowInitMapping.csx", + "code": "" + } + } + ] + }, + "states": [ + { + "key": "flow-init-state", + "stateType": 1, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Flow Init State" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "flow-types-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/FlowInitMapping.csx", + "code": "" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "auto-flow-to-complete", + "target": "flow-completed", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to Flow Completed" + } + ], + "rule": { + "location": "./src/AlwaysTrueRule.csx", + "code": "" + }, + "onExecutionTasks": [] + } + ] + }, + { + "key": "flow-completed", + "stateType": 3, + "subType": 1, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Flow Completed (Final)" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [] + } + ] + } +} diff --git a/core/Workflows/flow-types/src/FlowInitMapping.csx b/core/Workflows/flow-types/src/FlowInitMapping.csx new file mode 100644 index 0000000..d4f27c8 --- /dev/null +++ b/core/Workflows/flow-types/src/FlowInitMapping.csx @@ -0,0 +1,25 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; +using BBT.Workflow.Definitions; + +public class FlowInitMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public Task OutputHandler(ScriptContext context) + { + dynamic result = new ExpandoObject(); + result.flowType = "F"; + result.flowInitialized = true; + result.initAt = DateTime.UtcNow.ToString("o"); + + LogInformation("FlowInitMapping completed - Flow type test"); + + return Task.FromResult(new ScriptResponse { Data = result }); + } +} diff --git a/core/Workflows/flow-types/src/SubFlowInitMapping.csx b/core/Workflows/flow-types/src/SubFlowInitMapping.csx new file mode 100644 index 0000000..15f14fb --- /dev/null +++ b/core/Workflows/flow-types/src/SubFlowInitMapping.csx @@ -0,0 +1,25 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; +using BBT.Workflow.Definitions; + +public class SubFlowInitMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public Task OutputHandler(ScriptContext context) + { + dynamic result = new ExpandoObject(); + result.flowType = "S"; + result.subFlowInitialized = true; + result.initAt = DateTime.UtcNow.ToString("o"); + + LogInformation("SubFlowInitMapping completed - SubFlow type test"); + + return Task.FromResult(new ScriptResponse { Data = result }); + } +} diff --git a/core/Workflows/flow-types/subflow-flow-test.json b/core/Workflows/flow-types/subflow-flow-test.json new file mode 100644 index 0000000..667198f --- /dev/null +++ b/core/Workflows/flow-types/subflow-flow-test.json @@ -0,0 +1,122 @@ +{ + "key": "subflow-flow-test", + "flow": "sys-flows", + "flowVersion": "1.0.0", + "domain": "core", + "version": "1.0.0", + "tags": [ + "integration-test", + "flow-types", + "subflow" + ], + "attributes": { + "type": "S", + "timeout": null, + "errorBoundary": null, + "labels": [ + { + "language": "en-US", + "label": "SubFlow Type (S) Integration Test" + } + ], + "functions": [], + "features": [], + "extensions": [], + "sharedTransitions": [], + "startTransition": { + "key": "start-subflow-flow-test", + "target": "subflow-init-state", + "triggerType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Start SubFlow Type Test" + } + ], + "onExecutionTasks": [ + { + "order": 1, + "task": { + "key": "flow-types-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/SubFlowInitMapping.csx", + "code": "" + } + } + ] + }, + "states": [ + { + "key": "subflow-init-state", + "stateType": 1, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "SubFlow Init State" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "flow-types-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/SubFlowInitMapping.csx", + "code": "" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "auto-subflow-to-complete", + "target": "subflow-completed", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to SubFlow Completed" + } + ], + "rule": { + "location": "./src/AlwaysTrueRule.csx", + "code": "" + }, + "onExecutionTasks": [] + } + ] + }, + { + "key": "subflow-completed", + "stateType": 3, + "subType": 1, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "SubFlow Completed (Final)" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [] + } + ] + } +} diff --git a/core/Workflows/instance-management/instance-management-test-workflow.json b/core/Workflows/instance-management/instance-management-test-workflow.json index 4977b18..3802507 100644 --- a/core/Workflows/instance-management/instance-management-test-workflow.json +++ b/core/Workflows/instance-management/instance-management-test-workflow.json @@ -25,7 +25,7 @@ "versionStrategy": "Major", "timer": { "reset": "false", - "duration": "PT120S" + "duration": "PT80S" } }, "labels": [ diff --git a/doc/integration-test-documentation.md b/doc/integration-test-documentation.md index dd753e3..3e8025e 100644 --- a/doc/integration-test-documentation.md +++ b/doc/integration-test-documentation.md @@ -15,7 +15,7 @@ Bu dokuman, `core` projesindeki tum integration test workflow'larini, icerdikler | 5 | View, Function & Extension | `view-function-extension-test-workflow` | View tipleri, display modlari, function, extension, wizard state, **features** referansi | | 6 | Schema & Data | `schema-data-test-workflow` | Master schema, transition schema, updateData, field roles | | 7 | Instance Management | `instance-management-test-workflow` | Filtering, pagination, sorting, timeout, idempotent start, **subType 4/5/6** (suspended/busy/human) | -| 8 | Flow Types | `core-flow-test` + `subprocess-flow-test` | **Core (C)** ve **SubProcess (P)** workflow tipleri | +| 8 | Flow Types | `core-flow-test` + `subprocess-flow-test` + `flow-flow-test` + `subflow-flow-test` | **Core (C)**, **SubProcess (P)**, **Flow (F)** ve **SubFlow (S)** workflow tipleri | | 9 | Version Consistency | `version-consistency-test-workflow` (v1.0.0 + v2.0.0) | Workflow versiyon degisiminde mevcut instance'larin kendi versiyonlariyla devam etmesi | **Grup numaralari:** Eski **Grup 8 (Extended Tasks)** icerigi **Grup 3 (Task Execution)** altinda birlestirilmistir (`extended-tasks-test-workflow` artik Grup 3 kapsaminda). Matris ve tablolarda **G8 / Grup 8** = Flow Types, **G9 / Grup 9** = Version Consistency. @@ -810,8 +810,10 @@ instance-management-test-workflow (F) **Workflow'lar:** - `core-flow-test` (type: C) - Core flow tipi - `subprocess-flow-test` (type: P) - SubProcess flow tipi +- `flow-flow-test` (type: F) - Flow tipi +- `subflow-flow-test` (type: S) - SubFlow tipi -**Amac:** vNext'in destekledigi 4 farkli workflow tipinden (C, F, S, P) test edilmeyen **Core (C)** ve **SubProcess (P)** tiplerinin runtime tarafindan kabul edilip calistirilabildigini dogrular. +**Amac:** vNext'in destekledigi 4 farkli workflow tipinden (C, F, S, P) her birinin runtime tarafindan kabul edilip calistirilabildigini dogrular. F ve S tipleri diger gruplarda kapsamli test edilir; burada yalnizca minimal smoke dogrulamasi yapilir. ### Agac Yapisi @@ -845,6 +847,36 @@ subprocess-flow-test (P) │ └── rule: AlwaysTrueRule.csx │ └── subprocess-completed (Final/Success, stateType:3, subType:1) + +flow-flow-test (F) +│ +├── [startTransition] start-flow-flow-test → flow-init-state +│ └── onExecutionTasks: +│ └── flow-types-script-task + FlowInitMapping.csx +│ +├── flow-init-state (Initial, stateType:1) +│ ├── onEntries: +│ │ └── flow-types-script-task + FlowInitMapping.csx +│ └── Transitions: +│ └── auto-flow-to-complete (Auto) → flow-completed +│ └── rule: AlwaysTrueRule.csx +│ +└── flow-completed (Final/Success, stateType:3, subType:1) + +subflow-flow-test (S) +│ +├── [startTransition] start-subflow-flow-test → subflow-init-state +│ └── onExecutionTasks: +│ └── flow-types-script-task + SubFlowInitMapping.csx +│ +├── subflow-init-state (Initial, stateType:1) +│ ├── onEntries: +│ │ └── flow-types-script-task + SubFlowInitMapping.csx +│ └── Transitions: +│ └── auto-subflow-to-complete (Auto) → subflow-completed +│ └── rule: AlwaysTrueRule.csx +│ +└── subflow-completed (Final/Success, stateType:3, subType:1) ``` ### Test Edilen Ozellikler @@ -853,7 +885,9 @@ subprocess-flow-test (P) |---------|-------------------|---------------| | Core flow tipi (type: C) | Minimal workflow publish ve calistirma | core-flow-test | | SubProcess flow tipi (type: P) | Minimal workflow publish ve calistirma | subprocess-flow-test | -| Farkli flow tiplerinin runtime uyumlulugu | Tum 4 tip: C (G8), F (G1-G7, G9), S (G2), P (G8) | Tum gruplarda | +| Flow tipi (type: F) | Minimal workflow publish ve calistirma | flow-flow-test | +| SubFlow tipi (type: S) | Minimal workflow publish ve calistirma | subflow-flow-test | +| Farkli flow tiplerinin runtime uyumlulugu | Tum 4 tip: C, F, S, P (hepsi G8'de minimal smoke; F/S ayrica G1-G7, G2, G9'da kapsamli) | Tum gruplarda | ### Kullanilan Bilesenler @@ -862,6 +896,8 @@ subprocess-flow-test (P) | Task | `flow-types-script-task` (type:7) | Tasks/flow-types/flow-types-script-task.json | | CSX | CoreInitMapping | Workflows/flow-types/src/CoreInitMapping.csx | | CSX | SubProcessInitMapping | Workflows/flow-types/src/SubProcessInitMapping.csx | +| CSX | FlowInitMapping | Workflows/flow-types/src/FlowInitMapping.csx | +| CSX | SubFlowInitMapping | Workflows/flow-types/src/SubFlowInitMapping.csx | | CSX | AlwaysTrueRule | Workflows/flow-types/src/AlwaysTrueRule.csx | ### HTTP Test Dosyasi (`flow-types-test.http`) @@ -870,6 +906,8 @@ subprocess-flow-test (P) |------|---------------| | Core flow | Core (C) tipi workflow baslatma ve state kontrolu | | SubProcess flow | SubProcess (P) tipi workflow baslatma ve state kontrolu | +| Flow | Flow (F) tipi workflow baslatma ve state kontrolu | +| SubFlow | SubFlow (S) tipi workflow baslatma ve state kontrolu | --- diff --git a/tests/Core/Helpers/InstanceListJson.cs b/tests/Core/Helpers/InstanceListJson.cs index cd59b7d..f135b61 100644 --- a/tests/Core/Helpers/InstanceListJson.cs +++ b/tests/Core/Helpers/InstanceListJson.cs @@ -77,6 +77,30 @@ public static bool ContainsInstanceId(JsonElement body, string instanceId) return false; } + /// + /// Returns the zero-based position of the row whose id equals , + /// or -1 when not found. Useful for asserting sort order (e.g. sort=-createdAt). + /// + public static int IndexOfInstanceId(JsonElement body, string instanceId) + { + if (string.IsNullOrEmpty(instanceId)) + return -1; + + var items = ExtractItems(body); + for (int i = 0; i < items.Count; i++) + { + if ( + string.Equals( + TryGetId(items[i]), + instanceId, + StringComparison.Ordinal + ) + ) + return i; + } + return -1; + } + /// /// Returns attributes object from an instance row, or null. /// diff --git a/tests/Core/Helpers/WorkflowInstanceTestHelper.cs b/tests/Core/Helpers/WorkflowInstanceTestHelper.cs index fd27dc0..e1263f1 100644 --- a/tests/Core/Helpers/WorkflowInstanceTestHelper.cs +++ b/tests/Core/Helpers/WorkflowInstanceTestHelper.cs @@ -1,3 +1,4 @@ +using System.Globalization; using System.Net; using System.Text.Json; using VNext.Testing.Sdk.Client; @@ -145,4 +146,53 @@ bool expectAllowed else Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } + + /// + /// Returns the attributes object from GET .../instances/{id}. + /// Fails the test when the response does not include an attributes property. + /// + public async Task GetAttributesAsync(string instanceId) + { + var response = await _api.GetInstanceAsync(_workflowKey, instanceId); + Assert.True( + response.Body.TryGetProperty("attributes", out var attributes), + "GetInstance response should include 'attributes'." + ); + return attributes; + } + + /// + /// Calls GET .../instances with any subset of filter / sort / page / + /// pageSize query parameters and asserts HTTP 200 before returning the response body. + /// is the GraphQL / JSON filter string expected by the runtime + /// (see vnext-runtime instance-filtering doc; e.g. {"attributes":{"category":{"eq":"finance"}}}). + /// + public async Task ListInstancesAsync( + string? filterJson = null, + string? sort = null, + int? page = null, + int? pageSize = null, + Dictionary? extraQuery = null + ) + { + var qp = new Dictionary(); + if (!string.IsNullOrEmpty(filterJson)) + qp["filter"] = filterJson; + if (!string.IsNullOrEmpty(sort)) + qp["sort"] = sort; + if (page.HasValue) + qp["page"] = page.Value.ToString(CultureInfo.InvariantCulture); + if (pageSize.HasValue) + qp["pageSize"] = pageSize.Value.ToString(CultureInfo.InvariantCulture); + + if (extraQuery is not null) + { + foreach (var kv in extraQuery) + qp[kv.Key] = kv.Value; + } + + var response = await _api.ListInstancesAsync(_workflowKey, qp); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + return response.Body; + } } diff --git a/tests/Core/Tests/flow-types/FlowTypesTests.cs b/tests/Core/Tests/flow-types/FlowTypesTests.cs new file mode 100644 index 0000000..9984993 --- /dev/null +++ b/tests/Core/Tests/flow-types/FlowTypesTests.cs @@ -0,0 +1,78 @@ +using System.Text.Json; +using Core.IntegrationTests.Helpers; +using Core.IntegrationTests.Infrastructure; +using Xunit; + +namespace Core.IntegrationTests.Tests.FlowTypes; + +/// +/// Smoke tests for all four vNext workflow types (C, P, F, S). +/// Each workflow in core/Workflows/flow-types/ follows the same minimal pattern: +/// start → init-state (onEntries mapping writes flowType) → auto transition → completed (final). +/// The tests verify: +/// +/// Instance starts successfully (runtime accepts the flow type). +/// Auto transition fires and the instance reaches the final state with status: "C". +/// Init mapping writes the expected flowType attribute. +/// +/// +public class FlowTypesTests : IntegrationTestBase +{ + private static readonly TimeSpan StateTimeout = TimeSpan.FromSeconds(30); + + public FlowTypesTests(VNextTestEnvironment environment) + : base(environment) { } + + [Fact] + public async Task CoreFlowType_StartsAndCompletesSuccessfully() + { + await AssertFlowTypeSmoke("core-flow-test", "core-completed", "C"); + } + + [Fact] + public async Task SubProcessFlowType_StartsAndCompletesSuccessfully() + { + await AssertFlowTypeSmoke("subprocess-flow-test", "subprocess-completed", "P"); + } + + [Fact] + public async Task FlowFlowType_StartsAndCompletesSuccessfully() + { + await AssertFlowTypeSmoke("flow-flow-test", "flow-completed", "F"); + } + + [Fact] + public async Task SubFlowFlowType_StartsAndCompletesSuccessfully() + { + await AssertFlowTypeSmoke("subflow-flow-test", "subflow-completed", "S"); + } + + private async Task AssertFlowTypeSmoke( + string workflowKey, + string expectedFinalState, + string expectedFlowType + ) + { + var wf = new WorkflowInstanceTestHelper(Api, workflowKey); + var body = new + { + key = WorkflowInstanceTestHelper.UniqueInstanceKey($"flow-type-{expectedFlowType}"), + tags = new[] { "integration-test", "flow-types", expectedFlowType }, + attributes = new { }, + }; + + var instanceId = await wf.StartInstanceIdAsync(body); + await wf.WaitForStateAsync(instanceId, expectedFinalState, StateTimeout); + + var stateBody = await wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("C", StateFunctionJson.ExtractStatus(stateBody)); + + var attrs = await wf.GetAttributesAsync(instanceId); + JsonElementAssertions.AssertPropertyString( + attrs, + "flowType", + expectedFlowType, + $"Init mapping should write flowType = \"{expectedFlowType}\"." + ); + } +} diff --git a/tests/Core/Tests/instance-management-test-workflow/InstanceManagementScenarioActions.cs b/tests/Core/Tests/instance-management-test-workflow/InstanceManagementScenarioActions.cs new file mode 100644 index 0000000..d1d198f --- /dev/null +++ b/tests/Core/Tests/instance-management-test-workflow/InstanceManagementScenarioActions.cs @@ -0,0 +1,109 @@ +using Core.IntegrationTests.Helpers; +using Xunit; + +namespace Core.IntegrationTests.Tests.InstanceManagementTestWorkflow; + +/// +/// Scenario actions tied to instance-management-test-workflow. +/// Encapsulates workflow-specific literals (state names such as active-state, +/// processing-state; the process transition key; start body tag / attribute shape) +/// so the test class stays focused on assertions. +/// +/// Per vnext-tests-as-code layout rules: logic bound to the data contract or state literals of a +/// single workflow lives next to that scenario's *Tests.cs file, not under +/// tests/Core/Helpers. Generic primitives (HTTP orchestration, list JSON parsing, JSON +/// assertions) stay in / +/// / . +/// +/// +public sealed class InstanceManagementScenarioActions +{ + private readonly WorkflowInstanceTestHelper _wf; + private readonly TimeSpan _shortTimeout; + + public InstanceManagementScenarioActions(WorkflowInstanceTestHelper wf, TimeSpan shortTimeout) + { + _wf = wf; + _shortTimeout = shortTimeout; + } + + /// + /// Starts an instance with optional / . + /// When both are null, the start body omits attributes entirely so + /// InitInstanceMgmtMapping applies defaults (category = "default", priority = 1). + /// + public Task StartDefaultInstanceAsync(string? category, int? priority) => + _wf.StartInstanceIdAsync(BuildStartBody(category, priority, tagSuffix: "default")); + + /// + /// Starts an instance and waits until it reaches active-state. + /// List / filter / sort tests need a well-defined source state per instance. + /// + public async Task StartAndAdvanceToActiveAsync(string? category, int? priority) + { + var instanceId = await _wf.StartInstanceIdAsync( + BuildStartBody(category, priority, tagSuffix: "list-filter") + ); + await _wf.WaitForStateAsync(instanceId, "active-state", _shortTimeout); + return instanceId; + } + + /// + /// Runs the active-state → processing-state → <expectedFinalState> chain using the + /// process transition followed by (e.g. + /// suspend, set-busy, assign-human). Asserts the terminal instance + /// status is "C". + /// + public async Task RunThreeStepTransitionAsync( + string instanceId, + string processingTransition, + string expectedFinalState + ) + { + await _wf.WaitForStateAsync(instanceId, "active-state", _shortTimeout); + await _wf.RunTransitionAsync(instanceId, "process", headers: null); + await _wf.WaitForStateAsync(instanceId, "processing-state", _shortTimeout); + await _wf.RunTransitionAsync(instanceId, processingTransition, headers: null); + await _wf.WaitForStateAsync(instanceId, expectedFinalState, _shortTimeout); + + var body = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + // On reaching a final state, status should be "C" (happy-path terminal). + Assert.Equal("C", StateFunctionJson.ExtractStatus(body)); + } + + /// + /// Builds the start body used across this workflow's integration tests. The tags array + /// carries a scenario-specific suffix so filter / list queries can be scoped when debugging. + /// + public static object BuildStartBody(string? category, int? priority, string tagSuffix) + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey($"instance-mgmt-{tagSuffix}"); + var tags = new[] { "integration-test", "instance-management", tagSuffix }; + + if (category is null && priority is null) + return new { key, tags }; + + if (category is null) + return new + { + key, + tags, + attributes = new { priority = priority!.Value }, + }; + + if (priority is null) + return new + { + key, + tags, + attributes = new { category }, + }; + + return new + { + key, + tags, + attributes = new { category, priority = priority.Value }, + }; + } +} diff --git a/tests/Core/Tests/instance-management-test-workflow/InstanceManagementTestWorkflowTests.cs b/tests/Core/Tests/instance-management-test-workflow/InstanceManagementTestWorkflowTests.cs index b121969..0a1b330 100644 --- a/tests/Core/Tests/instance-management-test-workflow/InstanceManagementTestWorkflowTests.cs +++ b/tests/Core/Tests/instance-management-test-workflow/InstanceManagementTestWorkflowTests.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Text.Json; -using System.Threading.Tasks; using Core.IntegrationTests.Helpers; using Core.IntegrationTests.Infrastructure; using Xunit; @@ -21,19 +16,32 @@ namespace Core.IntegrationTests.Tests.InstanceManagementTestWorkflow; /// Workflow timeout (PT120S → timeout-state subType 3) /// Idempotent start (two POSTs with the same key return the same instance id) /// Instance list filtering — GraphQL / JSON shape (attributes, currentState, status) -/// Pagination (page / pageSize); list sort by createdAt asc/desc without filter where sort is isolated +/// Pagination (page / pageSize); list sort by createdAt using runtime sort / orderBy JSON (vnext-runtime/doc/tr/flow/instance-filtering.md, OrderBy / Sort) /// InitInstanceMgmtMapping contract (category / priority / testStarted / startedAt) /// /// -/// Notes: -/// - The filter parameter is always in GraphQL / JSON form (vnext-workflow-creation §7). -/// - The SDK applies HttpClient URL encoding via ListInstancesAsync query-parameter dictionary; -/// when the filter JSON string is passed as a dictionary value, {/}/" are encoded automatically. +/// Layout: +/// +/// Generic primitives live in / +/// / . +/// Workflow-specific start-body shape and state literals live in +/// ; instance attributes contract +/// assertions live in . +/// /// public class InstanceManagementTestWorkflowTests : IntegrationTestBase { private const string WorkflowKey = "instance-management-test-workflow"; + /// + /// Runtime sort query shape (v0.0.37+): ?sort={"field":"createdAt","direction":"desc"} + /// — see vnext-runtime/doc/tr/flow/instance-filtering.md (OrderBy / Sort). + /// + private const string SortCreatedAtDesc = "{\"field\":\"createdAt\",\"direction\":\"desc\"}"; + + /// Same as with direction: asc (default when omitted at API level). + private const string SortCreatedAtAsc = "{\"field\":\"createdAt\",\"direction\":\"asc\"}"; + /// Tolerance window for short manual-transition flows. private static readonly TimeSpan ShortStateTimeout = TimeSpan.FromSeconds(30); @@ -44,11 +52,13 @@ public class InstanceManagementTestWorkflowTests : IntegrationTestBase private static readonly TimeSpan WorkflowTimeoutWait = TimeSpan.FromMinutes(3); private readonly WorkflowInstanceTestHelper _wf; + private readonly InstanceManagementScenarioActions _scenario; public InstanceManagementTestWorkflowTests(VNextTestEnvironment environment) : base(environment) { _wf = new WorkflowInstanceTestHelper(Api, WorkflowKey); + _scenario = new InstanceManagementScenarioActions(_wf, ShortStateTimeout); } // ----------------------------------------------------------------------- @@ -58,7 +68,7 @@ public InstanceManagementTestWorkflowTests(VNextTestEnvironment environment) [Fact] public async Task HappyPath_ProcessThenFinish_ReachesCompletedState() { - var instanceId = await StartDefaultInstanceAsync("finance", 1); + var instanceId = await _scenario.StartDefaultInstanceAsync("finance", 1); await _wf.WaitForStateAsync(instanceId, "active-state", ShortStateTimeout); await _wf.RunTransitionAsync(instanceId, "process", headers: null); @@ -71,7 +81,7 @@ public async Task HappyPath_ProcessThenFinish_ReachesCompletedState() var body = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); Assert.Equal("C", StateFunctionJson.ExtractStatus(body)); - var attrs = await GetAttributesAsync(WorkflowKey, instanceId); + var attrs = await _wf.GetAttributesAsync(instanceId); InstanceManagementInstanceDataAssertions.AssertInitialAttributes( attrs, expectedCategory: "finance", @@ -82,7 +92,7 @@ public async Task HappyPath_ProcessThenFinish_ReachesCompletedState() [Fact] public async Task FastComplete_FromActive_ReachesCompletedStateWithoutProcessing() { - var instanceId = await StartDefaultInstanceAsync(category: null, priority: null); + var instanceId = await _scenario.StartDefaultInstanceAsync(category: null, priority: null); await _wf.WaitForStateAsync(instanceId, "active-state", ShortStateTimeout); await _wf.RunTransitionAsync(instanceId, "fast-complete", headers: null); @@ -92,7 +102,7 @@ public async Task FastComplete_FromActive_ReachesCompletedStateWithoutProcessing Assert.Equal("C", StateFunctionJson.ExtractStatus(body)); // When category/priority are omitted from the start body, init mapping applies defaults. - var attrs = await GetAttributesAsync(WorkflowKey, instanceId); + var attrs = await _wf.GetAttributesAsync(instanceId); InstanceManagementInstanceDataAssertions.AssertInitialAttributes( attrs, expectedCategory: "default", @@ -103,7 +113,7 @@ public async Task FastComplete_FromActive_ReachesCompletedStateWithoutProcessing [Fact] public async Task RejectPath_ReachesRejectedState_SubType2_StillCompleted() { - var instanceId = await StartDefaultInstanceAsync(category: "risky", priority: 3); + var instanceId = await _scenario.StartDefaultInstanceAsync(category: "risky", priority: 3); await _wf.WaitForStateAsync(instanceId, "active-state", ShortStateTimeout); await _wf.RunTransitionAsync(instanceId, "process", headers: null); @@ -120,8 +130,8 @@ public async Task RejectPath_ReachesRejectedState_SubType2_StillCompleted() [Fact] public async Task SuspendPath_ReachesSuspendedState_SubType4() { - var instanceId = await StartDefaultInstanceAsync(category: "ops", priority: 2); - await RunThreeStepTransitionAsync(instanceId, "suspend", "suspended-state"); + var instanceId = await _scenario.StartDefaultInstanceAsync(category: "ops", priority: 2); + await _scenario.RunThreeStepTransitionAsync(instanceId, "suspend", "suspended-state"); // subType:4 (Temporarily Suspended) — runtime state subType filtering treats this final state as Suspended (see instance-filtering.md "Instance Subtype (4,5,6) Filtering"). } @@ -129,15 +139,15 @@ public async Task SuspendPath_ReachesSuspendedState_SubType4() [Fact] public async Task BusyPath_ReachesBusyState_SubType5() { - var instanceId = await StartDefaultInstanceAsync(category: "ops", priority: 2); - await RunThreeStepTransitionAsync(instanceId, "set-busy", "busy-state"); + var instanceId = await _scenario.StartDefaultInstanceAsync(category: "ops", priority: 2); + await _scenario.RunThreeStepTransitionAsync(instanceId, "set-busy", "busy-state"); } [Fact] public async Task HumanPath_ReachesHumanState_SubType6() { - var instanceId = await StartDefaultInstanceAsync(category: "ops", priority: 2); - await RunThreeStepTransitionAsync(instanceId, "assign-human", "human-state"); + var instanceId = await _scenario.StartDefaultInstanceAsync(category: "ops", priority: 2); + await _scenario.RunThreeStepTransitionAsync(instanceId, "assign-human", "human-state"); } // ----------------------------------------------------------------------- @@ -168,12 +178,17 @@ public async Task IdempotentStart_SameKey_ReturnsSameInstanceId() [Fact] public async Task Filter_ByCategoryFinance_ListContainsOnlyFinanceInstances() { - var financeId = await StartAndAdvanceToActiveAsync(category: "finance", priority: 1); - var opsId = await StartAndAdvanceToActiveAsync(category: "ops", priority: 2); + var financeId = await _scenario.StartAndAdvanceToActiveAsync( + category: "finance", + priority: 1 + ); + var opsId = await _scenario.StartAndAdvanceToActiveAsync(category: "ops", priority: 2); // filter={"attributes":{"category":{"eq":"finance"}}} - var body = await ListInstancesWithFilterAsync( - "{\"attributes\":{\"category\":{\"eq\":\"finance\"}}}" + var body = await _wf.ListInstancesAsync( + filterJson: "{\"attributes\":{\"category\":{\"eq\":\"finance\"}}}", + sort: SortCreatedAtDesc, + pageSize: 100 ); Assert.True( @@ -201,14 +216,22 @@ public async Task Filter_ByCategoryFinance_ListContainsOnlyFinanceInstances() [Fact] public async Task Filter_ByCurrentState_ActiveState_ReturnsInstancesInActiveState() { - var activeId = await StartAndAdvanceToActiveAsync(category: "finance", priority: 1); - var completedId = await StartAndAdvanceToActiveAsync(category: "ops", priority: 1); + var activeId = await _scenario.StartAndAdvanceToActiveAsync( + category: "finance", + priority: 1 + ); + var completedId = await _scenario.StartAndAdvanceToActiveAsync( + category: "ops", + priority: 1 + ); await _wf.RunTransitionAsync(completedId, "fast-complete", headers: null); await _wf.WaitForStateAsync(completedId, "completed-state", ShortStateTimeout); // filter={"currentState":{"eq":"active-state"}} - var body = await ListInstancesWithFilterAsync( - "{\"currentState\":{\"eq\":\"active-state\"}}" + var body = await _wf.ListInstancesAsync( + filterJson: "{\"currentState\":{\"eq\":\"active-state\"}}", + sort: SortCreatedAtDesc, + pageSize: 100 ); Assert.True( @@ -224,13 +247,23 @@ public async Task Filter_ByCurrentState_ActiveState_ReturnsInstancesInActiveStat [Fact] public async Task Filter_ByStatus_ActiveCode_ReturnsOnlyActiveInstances() { - var activeId = await StartAndAdvanceToActiveAsync(category: "finance", priority: 1); - var completedId = await StartAndAdvanceToActiveAsync(category: "ops", priority: 1); + var activeId = await _scenario.StartAndAdvanceToActiveAsync( + category: "finance", + priority: 1 + ); + var completedId = await _scenario.StartAndAdvanceToActiveAsync( + category: "ops", + priority: 1 + ); await _wf.RunTransitionAsync(completedId, "fast-complete", headers: null); await _wf.WaitForStateAsync(completedId, "completed-state", ShortStateTimeout); // filter={"status":{"eq":"A"}} — status code "A" (Active). - var body = await ListInstancesWithFilterAsync("{\"status\":{\"eq\":\"A\"}}"); + var body = await _wf.ListInstancesAsync( + filterJson: "{\"status\":{\"eq\":\"A\"}}", + sort: SortCreatedAtDesc, + pageSize: 100 + ); Assert.True( InstanceListJson.ContainsInstanceId(body, activeId), @@ -249,29 +282,25 @@ public async Task Filter_ByStatus_ActiveCode_ReturnsOnlyActiveInstances() [Fact] public async Task Sort_DescendingByCreatedAt_NewestInstanceAppearsBeforeOldest() { - var firstId = await StartAndAdvanceToActiveAsync(category: "finance", priority: 1); - // Short delay so createdAt ordering is distinguishable (precision may be sub-second). + var sortTag = $"sort-desc-{Guid.NewGuid():N}"; + var firstId = await _scenario.StartAndAdvanceToActiveAsync(category: sortTag, priority: 1); await Task.Delay(1200); - var secondId = await StartAndAdvanceToActiveAsync(category: "finance", priority: 1); + var secondId = await _scenario.StartAndAdvanceToActiveAsync(category: sortTag, priority: 1); - // Sort only (no filter) — isolates list sort behaviour. Large pageSize so both instances - // are likely on the first page in typical integration environments. - var queryParams = new Dictionary - { - ["sort"] = "-createdAt", - ["pageSize"] = "100", - }; - var response = await Api.ListInstancesAsync(WorkflowKey, queryParams); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await _wf.ListInstancesAsync( + filterJson: $"{{\"attributes\":{{\"category\":{{\"eq\":\"{sortTag}\"}}}}}}", + sort: SortCreatedAtDesc, + pageSize: 100 + ); - var firstIdx = IndexOfInstance(response.Body, firstId); - var secondIdx = IndexOfInstance(response.Body, secondId); + var firstIdx = InstanceListJson.IndexOfInstanceId(body, firstId); + var secondIdx = InstanceListJson.IndexOfInstanceId(body, secondId); Assert.True(firstIdx >= 0, $"first instance ({firstId}) should be listed."); Assert.True(secondIdx >= 0, $"second instance ({secondId}) should be listed."); Assert.True( secondIdx < firstIdx, - "Newest instance (second) should appear before the older one under sort=-createdAt; " + "Newest instance (second) should appear before the older one under sort with createdAt desc; " + $"secondIdx={secondIdx}, firstIdx={firstIdx}." ); } @@ -279,21 +308,19 @@ public async Task Sort_DescendingByCreatedAt_NewestInstanceAppearsBeforeOldest() [Fact] public async Task Sort_AscendingByCreatedAt_OldestInstanceAppearsBeforeNewest() { - var firstId = await StartAndAdvanceToActiveAsync(category: "finance", priority: 1); + var sortTag = $"sort-asc-{Guid.NewGuid():N}"; + var firstId = await _scenario.StartAndAdvanceToActiveAsync(category: sortTag, priority: 1); await Task.Delay(1200); - var secondId = await StartAndAdvanceToActiveAsync(category: "finance", priority: 1); + var secondId = await _scenario.StartAndAdvanceToActiveAsync(category: sortTag, priority: 1); - var queryParams = new Dictionary - { - // Ascending creation time — same shorthand family as `-createdAt` / GetInstances doc (`FieldName`). - ["sort"] = "createdAt", - ["pageSize"] = "100", - }; - var response = await Api.ListInstancesAsync(WorkflowKey, queryParams); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await _wf.ListInstancesAsync( + filterJson: $"{{\"attributes\":{{\"category\":{{\"eq\":\"{sortTag}\"}}}}}}", + sort: SortCreatedAtAsc, + pageSize: 100 + ); - var firstIdx = IndexOfInstance(response.Body, firstId); - var secondIdx = IndexOfInstance(response.Body, secondId); + var firstIdx = InstanceListJson.IndexOfInstanceId(body, firstId); + var secondIdx = InstanceListJson.IndexOfInstanceId(body, secondId); Assert.True(firstIdx >= 0, $"first instance ({firstId}) should be listed."); Assert.True(secondIdx >= 0, $"second instance ({secondId}) should be listed."); @@ -310,31 +337,28 @@ public async Task Pagination_WithPageSize_LimitsPageItemsAndReportsTotalCount() // Create at least three distinct finance instances. var ids = new List { - await StartAndAdvanceToActiveAsync(category: "finance", priority: 1), - await StartAndAdvanceToActiveAsync(category: "finance", priority: 1), - await StartAndAdvanceToActiveAsync(category: "finance", priority: 1), + await _scenario.StartAndAdvanceToActiveAsync(category: "finance", priority: 1), + await _scenario.StartAndAdvanceToActiveAsync(category: "finance", priority: 1), + await _scenario.StartAndAdvanceToActiveAsync(category: "finance", priority: 1), }; - var queryParams = new Dictionary - { - ["filter"] = "{\"attributes\":{\"category\":{\"eq\":\"finance\"}}}", - ["page"] = "1", - ["pageSize"] = "2", - ["sort"] = "-createdAt", - }; - var response = await Api.ListInstancesAsync(WorkflowKey, queryParams); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await _wf.ListInstancesAsync( + filterJson: "{\"attributes\":{\"category\":{\"eq\":\"finance\"}}}", + sort: SortCreatedAtDesc, + page: 1, + pageSize: 2 + ); - var items = InstanceListJson.ExtractItems(response.Body); + var items = InstanceListJson.ExtractItems(body); Assert.True( items.Count <= 2, $"page=1&pageSize=2 should cap returned items at 2; got {items.Count}." ); // If a pagination object exists, page/pageSize should be reported correctly. - var page = InstanceListJson.TryGetPaginationInt(response.Body, "page"); - var pageSize = InstanceListJson.TryGetPaginationInt(response.Body, "pageSize"); - var totalCount = InstanceListJson.TryGetPaginationInt(response.Body, "totalCount"); + var page = InstanceListJson.TryGetPaginationInt(body, "page"); + var pageSize = InstanceListJson.TryGetPaginationInt(body, "pageSize"); + var totalCount = InstanceListJson.TryGetPaginationInt(body, "totalCount"); if (page is not null) Assert.Equal(1, page); if (pageSize is not null) @@ -357,7 +381,10 @@ await StartAndAdvanceToActiveAsync(category: "finance", priority: 1), [Fact] public async Task WorkflowTimeout_AfterPT120S_InstanceAutoMovesToTimeoutState() { - var instanceId = await StartAndAdvanceToActiveAsync(category: "timeout-test", priority: 1); + var instanceId = await _scenario.StartAndAdvanceToActiveAsync( + category: "timeout-test", + priority: 1 + ); // If we leave before 120s the assertion is meaningless; tolerate up to ~3 min poll. await _wf.WaitForStateAsync(instanceId, "timeout-state", WorkflowTimeoutWait); @@ -367,124 +394,4 @@ public async Task WorkflowTimeout_AfterPT120S_InstanceAutoMovesToTimeoutState() // (F = Faulted; no exception here — runtime treats timeout as normal terminal). Assert.Equal("C", StateFunctionJson.ExtractStatus(body)); } - - // ----------------------------------------------------------------------- - // Private helpers - // ----------------------------------------------------------------------- - - /// - /// category and priority are optional; when null the start body omits those fields - /// and init mapping writes defaults ("default" / 1). - /// - private Task StartDefaultInstanceAsync(string? category, int? priority) => - _wf.StartInstanceIdAsync( - BuildStartBody(category, priority, tagSuffix: "default") - ); - - /// - /// Start an instance and wait until it reaches active-state. - /// List/filter tests need a well-defined source state per instance. - /// - private async Task StartAndAdvanceToActiveAsync(string? category, int? priority) - { - var instanceId = await _wf.StartInstanceIdAsync( - BuildStartBody(category, priority, tagSuffix: "list-filter") - ); - await _wf.WaitForStateAsync(instanceId, "active-state", ShortStateTimeout); - return instanceId; - } - - private static object BuildStartBody(string? category, int? priority, string tagSuffix) - { - var key = WorkflowInstanceTestHelper.UniqueInstanceKey($"instance-mgmt-{tagSuffix}"); - var tags = new[] { "integration-test", "instance-management", tagSuffix }; - - if (category is null && priority is null) - return new - { - key, - tags, - }; - - if (category is null) - return new - { - key, - tags, - attributes = new { priority = priority!.Value }, - }; - - if (priority is null) - return new - { - key, - tags, - attributes = new { category }, - }; - - return new - { - key, - tags, - attributes = new { category, priority = priority.Value }, - }; - } - - private async Task RunThreeStepTransitionAsync( - string instanceId, - string processingTransition, - string expectedFinalState - ) - { - await _wf.WaitForStateAsync(instanceId, "active-state", ShortStateTimeout); - await _wf.RunTransitionAsync(instanceId, "process", headers: null); - await _wf.WaitForStateAsync(instanceId, "processing-state", ShortStateTimeout); - await _wf.RunTransitionAsync(instanceId, processingTransition, headers: null); - await _wf.WaitForStateAsync(instanceId, expectedFinalState, ShortStateTimeout); - - var body = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); - // On reaching a final state, status should be "C" (happy-path terminal). - Assert.Equal("C", StateFunctionJson.ExtractStatus(body)); - } - - private async Task ListInstancesWithFilterAsync(string filterJson) - { - var queryParams = new Dictionary - { - ["filter"] = filterJson, - // Large page size in test env — avoid coupling with pagination tests. - ["pageSize"] = "100", - ["sort"] = "-createdAt", - }; - var response = await Api.ListInstancesAsync(WorkflowKey, queryParams); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - return response.Body; - } - - private static int IndexOfInstance(JsonElement body, string instanceId) - { - var items = InstanceListJson.ExtractItems(body); - for (int i = 0; i < items.Count; i++) - { - if ( - string.Equals( - InstanceListJson.TryGetId(items[i]), - instanceId, - StringComparison.Ordinal - ) - ) - return i; - } - return -1; - } - - private async Task GetAttributesAsync(string workflowKey, string instanceId) - { - var response = await Api.GetInstanceAsync(workflowKey, instanceId); - Assert.True( - response.Body.TryGetProperty("attributes", out var attributes), - "GetInstance response should include 'attributes'." - ); - return attributes; - } } diff --git a/vnext-example.sln.DotSettings.user b/vnext-example.sln.DotSettings.user new file mode 100644 index 0000000..35732dd --- /dev/null +++ b/vnext-example.sln.DotSettings.user @@ -0,0 +1,4 @@ + + <SessionState ContinuousTestingMode="0" Name="All tests from Core.IntegrationTests.Tests.FlowTypes" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Namespace>Core.IntegrationTests.Tests.FlowTypes</Namespace> +</SessionState> \ No newline at end of file From 6b5211e045165c3743a50ded514bd4d7c3286d38 Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Mon, 4 May 2026 16:02:05 +0300 Subject: [PATCH 14/24] flow types are added and passed test. subflow tests are merged to one test. script base test logic added to docs. --- core.link.json | 2 +- .../subflow-test/child-data-extension.json | 30 -- .../grandchild-data-extension.json | 31 -- .../subflow-test/parent-data-extension.json | 30 -- .../src/ChildDataExtensionMapping.csx | 99 ----- .../src/GrandchildDataExtensionMapping.csx | 110 ----- .../src/ParentDataExtensionMapping.csx | 98 ----- .../start-busy-subprocess-task.json | 22 - .../trigger-busy-completion-task.json | 22 - .../subflow-test/child-workflow-view.json | 24 -- .../grandchild-workflow-view.json | 25 -- .../subflow-test/parent-workflow-view.json | 24 -- .../.meta/flow-flow-test.diagram.json | 3 + .../.meta/subflow-flow-test.diagram.json | 3 + core/Workflows/flow-types/flow-flow-test.json | 6 +- .../flow-types/subflow-flow-test.json | 6 +- .../subflow-orchestration-parent.json | 27 ++ .../busy-subprocess-workflow.diagram.json | 74 ---- .../subflow-view-test-child.diagram.json | 16 - .../subflow-view-test-grandchild.diagram.json | 20 - .../subflow-view-test-parent.diagram.json | 24 -- .../busy-subprocess-workflow.json | 163 -------- .../subflow-test/src/AlwaysTrueRule.csx | 14 - .../src/BusySubprocessStartMapping.csx | 58 --- .../subflow-test/src/ChildCompleteMapping.csx | 62 --- .../src/ChildSharedUpdateMapping.csx | 57 --- .../subflow-test/src/ChildStartMapping.csx | 59 --- .../src/ChildToGrandchildSubFlowMapping.csx | 117 ------ .../src/GrandchildCompleteMapping.csx | 63 --- .../src/GrandchildStartMapping.csx | 64 --- .../src/ParentCompleteMapping.csx | 63 --- .../ParentInitialGetInstanceDataMapping.csx | 31 -- .../src/ParentSharedUpdateMapping.csx | 58 --- .../subflow-test/src/ParentStartMapping.csx | 63 --- .../src/StartBusySubprocessMapping.csx | 89 ----- .../subflow-test/src/SubFlowMapping.csx | 111 ----- .../TriggerParentBusyCompletionMapping.csx | 121 ------ .../subflow-test/subflow-view-test-child.json | 364 ----------------- .../subflow-view-test-grandchild.json | 251 ------------ .../subflow-view-test-parent.json | 378 ------------------ doc/integration-test-documentation.md | 289 +++++++++---- vnext-example.sln.DotSettings.user | 4 - 42 files changed, 249 insertions(+), 2926 deletions(-) delete mode 100644 core/Extensions/subflow-test/child-data-extension.json delete mode 100644 core/Extensions/subflow-test/grandchild-data-extension.json delete mode 100644 core/Extensions/subflow-test/parent-data-extension.json delete mode 100644 core/Extensions/subflow-test/src/ChildDataExtensionMapping.csx delete mode 100644 core/Extensions/subflow-test/src/GrandchildDataExtensionMapping.csx delete mode 100644 core/Extensions/subflow-test/src/ParentDataExtensionMapping.csx delete mode 100644 core/Tasks/subflow-test/start-busy-subprocess-task.json delete mode 100644 core/Tasks/subflow-test/trigger-busy-completion-task.json delete mode 100644 core/Views/subflow-test/child-workflow-view.json delete mode 100644 core/Views/subflow-test/grandchild-workflow-view.json delete mode 100644 core/Views/subflow-test/parent-workflow-view.json create mode 100644 core/Workflows/flow-types/.meta/flow-flow-test.diagram.json create mode 100644 core/Workflows/flow-types/.meta/subflow-flow-test.diagram.json delete mode 100644 core/Workflows/subflow-test/.meta/busy-subprocess-workflow.diagram.json delete mode 100644 core/Workflows/subflow-test/.meta/subflow-view-test-child.diagram.json delete mode 100644 core/Workflows/subflow-test/.meta/subflow-view-test-grandchild.diagram.json delete mode 100644 core/Workflows/subflow-test/.meta/subflow-view-test-parent.diagram.json delete mode 100644 core/Workflows/subflow-test/busy-subprocess-workflow.json delete mode 100644 core/Workflows/subflow-test/src/AlwaysTrueRule.csx delete mode 100644 core/Workflows/subflow-test/src/BusySubprocessStartMapping.csx delete mode 100644 core/Workflows/subflow-test/src/ChildCompleteMapping.csx delete mode 100644 core/Workflows/subflow-test/src/ChildSharedUpdateMapping.csx delete mode 100644 core/Workflows/subflow-test/src/ChildStartMapping.csx delete mode 100644 core/Workflows/subflow-test/src/ChildToGrandchildSubFlowMapping.csx delete mode 100644 core/Workflows/subflow-test/src/GrandchildCompleteMapping.csx delete mode 100644 core/Workflows/subflow-test/src/GrandchildStartMapping.csx delete mode 100644 core/Workflows/subflow-test/src/ParentCompleteMapping.csx delete mode 100644 core/Workflows/subflow-test/src/ParentInitialGetInstanceDataMapping.csx delete mode 100644 core/Workflows/subflow-test/src/ParentSharedUpdateMapping.csx delete mode 100644 core/Workflows/subflow-test/src/ParentStartMapping.csx delete mode 100644 core/Workflows/subflow-test/src/StartBusySubprocessMapping.csx delete mode 100644 core/Workflows/subflow-test/src/SubFlowMapping.csx delete mode 100644 core/Workflows/subflow-test/src/TriggerParentBusyCompletionMapping.csx delete mode 100644 core/Workflows/subflow-test/subflow-view-test-child.json delete mode 100644 core/Workflows/subflow-test/subflow-view-test-grandchild.json delete mode 100644 core/Workflows/subflow-test/subflow-view-test-parent.json delete mode 100644 vnext-example.sln.DotSettings.user diff --git a/core.link.json b/core.link.json index 1b6eb5e..8652e01 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-05-04T11:35:58.531Z" + "importedAt": "2026-05-04T12:07:20.021Z" } \ No newline at end of file diff --git a/core/Extensions/subflow-test/child-data-extension.json b/core/Extensions/subflow-test/child-data-extension.json deleted file mode 100644 index d87c8f6..0000000 --- a/core/Extensions/subflow-test/child-data-extension.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "key": "child-data-extension", - "version": "1.0.0", - "domain": "core", - "flow": "sys-extensions", - "flowVersion": "1.0.0", - "tags": [ - "subflow-test", - "child-extension", - "data-extension", - "longpolling-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/ChildDataExtensionMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CgovLy8gPHN1bW1hcnk+Ci8vLyBDaGlsZCBEYXRhIEV4dGVuc2lvbiBNYXBwaW5nIC0gUHJvdmlkZXMgYWRkaXRpb25hbCBkYXRhIGZvciBjaGlsZCB3b3JrZmxvdyB2aWV3Ci8vLyBUaGlzIGV4dGVuc2lvbiBpcyB1c2VkIHRvIHRlc3QgbG9uZ3BvbGxpbmcgZnVuY3Rpb25hbGl0eSBmb3IgY2hpbGQgd29ya2Zsb3cgdmlld3MuCi8vLyA8L3N1bW1hcnk+CnB1YmxpYyBjbGFzcyBDaGlsZERhdGFFeHRlbnNpb25NYXBwaW5nIDogSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgdmFyIGh0dHBUYXNrID0gdGFzayBhcyBIdHRwVGFzazsKICAgICAgICAgICAgaWYgKGh0dHBUYXNrID09IG51bGwpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIHRocm93IG5ldyBJbnZhbGlkT3BlcmF0aW9uRXhjZXB0aW9uKCJUYXNrIG11c3QgYmUgYW4gSHR0cFRhc2siKTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgLy8gUHJlcGFyZSByZXF1ZXN0IGJvZHkgZm9yIGV4dGVuc2lvbiBkYXRhCiAgICAgICAgICAgIHZhciByZXF1ZXN0Qm9keSA9IG5ldwogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBleHRlbnNpb25UeXBlID0gImNoaWxkLWRhdGEtZXh0ZW5zaW9uIiwKICAgICAgICAgICAgICAgIGluc3RhbmNlSWQgPSBjb250ZXh0Lkluc3RhbmNlPy5JZCwKICAgICAgICAgICAgICAgIHdvcmtmbG93S2V5ID0gY29udGV4dC5Xb3JrZmxvdz8uS2V5LAogICAgICAgICAgICAgICAgcGFyZW50SW5zdGFuY2VJZCA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGE/LnBhcmVudEluc3RhbmNlSWQsCiAgICAgICAgICAgICAgICByZXF1ZXN0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdwogICAgICAgICAgICB9OwoKICAgICAgICAgICAgaHR0cFRhc2suU2V0Qm9keShyZXF1ZXN0Qm9keSk7CgogICAgICAgICAgICAvLyBTZXQgaGVhZGVycwogICAgICAgICAgICB2YXIgaGVhZGVycyA9IG5ldyBEaWN0aW9uYXJ5PHN0cmluZywgc3RyaW5nPz4KICAgICAgICAgICAgewogICAgICAgICAgICAgICAgWyJDb250ZW50LVR5cGUiXSA9ICJhcHBsaWNhdGlvbi9qc29uIiwKICAgICAgICAgICAgICAgIFsiWC1FeHRlbnNpb24tVHlwZSJdID0gImNoaWxkLWRhdGEiLAogICAgICAgICAgICAgICAgWyJYLVJlcXVlc3QtSWQiXSA9IEd1aWQuTmV3R3VpZCgpLlRvU3RyaW5nKCkKICAgICAgICAgICAgfTsKICAgICAgICAgICAgaHR0cFRhc2suU2V0SGVhZGVycyhoZWFkZXJzKTsKCiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgICAgIH0KICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGV4KQogICAgICAgIHsKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gImNoaWxkLWV4dGVuc2lvbi1pbnB1dC1lcnJvciIsCiAgICAgICAgICAgICAgICBEYXRhID0gbmV3IHsgZXJyb3IgPSBleC5NZXNzYWdlIH0KICAgICAgICAgICAgfSk7CiAgICAgICAgfQogICAgfQoKICAgIHB1YmxpYyBhc3luYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gImNoaWxkLWV4dGVuc2lvbi1zdWNjZXNzIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBjaGlsZEV4dGVuc2lvbiA9IG5ldwogICAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAgICAgZXh0ZW5zaW9uTmFtZSA9ICJjaGlsZC1kYXRhLWV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZSA9ICJjaGlsZC13b3JrZmxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgIGxvYWRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LAogICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gbmV3CiAgICAgICAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNoaWxkQ29uZmlnID0gbmV3CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNTdWJmbG93ID0gdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NQYXJlbnQgPSB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBsZXRpb25SZXF1aXJlZCA9IHRydWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGlsZE1ldGFkYXRhID0gbmV3CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd29ya2Zsb3dUeXBlID0gInN1YmZsb3ciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmVudFdvcmtmbG93ID0gInN1YmZsb3ctdmlldy10ZXN0LXBhcmVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXh0ZW5zaW9uVmVyc2lvbiA9ICIxLjAuMCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICBUYWdzID0gbmV3W10geyAic3ViZmxvdy10ZXN0IiwgImNoaWxkLWV4dGVuc2lvbiIsICJsb25ncG9sbGluZyIgfQogICAgICAgICAgICB9OwogICAgICAgIH0KICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGV4KQogICAgICAgIHsKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAiY2hpbGQtZXh0ZW5zaW9uLWVycm9yIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcgeyBlcnJvciA9IGV4Lk1lc3NhZ2UgfQogICAgICAgICAgICB9OwogICAgICAgIH0KICAgIH0KfQoK" - } - } - } -} \ No newline at end of file diff --git a/core/Extensions/subflow-test/grandchild-data-extension.json b/core/Extensions/subflow-test/grandchild-data-extension.json deleted file mode 100644 index a7a9bf1..0000000 --- a/core/Extensions/subflow-test/grandchild-data-extension.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "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/parent-data-extension.json b/core/Extensions/subflow-test/parent-data-extension.json deleted file mode 100644 index 780dc8f..0000000 --- a/core/Extensions/subflow-test/parent-data-extension.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "key": "parent-data-extension", - "version": "1.0.0", - "domain": "core", - "flow": "sys-extensions", - "flowVersion": "1.0.0", - "tags": [ - "subflow-test", - "parent-extension", - "data-extension", - "longpolling-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/ParentDataExtensionMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CgovLy8gPHN1bW1hcnk+Ci8vLyBQYXJlbnQgRGF0YSBFeHRlbnNpb24gTWFwcGluZyAtIFByb3ZpZGVzIGFkZGl0aW9uYWwgZGF0YSBmb3IgcGFyZW50IHdvcmtmbG93IHZpZXcKLy8vIFRoaXMgZXh0ZW5zaW9uIGlzIHVzZWQgdG8gdGVzdCBsb25ncG9sbGluZyBmdW5jdGlvbmFsaXR5IGZvciBwYXJlbnQgd29ya2Zsb3cgdmlld3MuCi8vLyA8L3N1bW1hcnk+CnB1YmxpYyBjbGFzcyBQYXJlbnREYXRhRXh0ZW5zaW9uTWFwcGluZyA6IElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIHZhciBodHRwVGFzayA9IHRhc2sgYXMgSHR0cFRhc2s7CiAgICAgICAgICAgIGlmIChodHRwVGFzayA9PSBudWxsKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgSW52YWxpZE9wZXJhdGlvbkV4Y2VwdGlvbigiVGFzayBtdXN0IGJlIGFuIEh0dHBUYXNrIik7CiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIC8vIFByZXBhcmUgcmVxdWVzdCBib2R5IGZvciBleHRlbnNpb24gZGF0YQogICAgICAgICAgICB2YXIgcmVxdWVzdEJvZHkgPSBuZXcKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgZXh0ZW5zaW9uVHlwZSA9ICJwYXJlbnQtZGF0YS1leHRlbnNpb24iLAogICAgICAgICAgICAgICAgaW5zdGFuY2VJZCA9IGNvbnRleHQuSW5zdGFuY2U/LklkLAogICAgICAgICAgICAgICAgd29ya2Zsb3dLZXkgPSBjb250ZXh0LldvcmtmbG93Py5LZXksCiAgICAgICAgICAgICAgICByZXF1ZXN0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdwogICAgICAgICAgICB9OwoKICAgICAgICAgICAgaHR0cFRhc2suU2V0Qm9keShyZXF1ZXN0Qm9keSk7CgogICAgICAgICAgICAvLyBTZXQgaGVhZGVycwogICAgICAgICAgICB2YXIgaGVhZGVycyA9IG5ldyBEaWN0aW9uYXJ5PHN0cmluZywgc3RyaW5nPz4KICAgICAgICAgICAgewogICAgICAgICAgICAgICAgWyJDb250ZW50LVR5cGUiXSA9ICJhcHBsaWNhdGlvbi9qc29uIiwKICAgICAgICAgICAgICAgIFsiWC1FeHRlbnNpb24tVHlwZSJdID0gInBhcmVudC1kYXRhIiwKICAgICAgICAgICAgICAgIFsiWC1SZXF1ZXN0LUlkIl0gPSBHdWlkLk5ld0d1aWQoKS5Ub1N0cmluZygpCiAgICAgICAgICAgIH07CiAgICAgICAgICAgIGh0dHBUYXNrLlNldEhlYWRlcnMoaGVhZGVycyk7CgogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9ICJwYXJlbnQtZXh0ZW5zaW9uLWlucHV0LWVycm9yIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcgeyBlcnJvciA9IGV4Lk1lc3NhZ2UgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAicGFyZW50LWV4dGVuc2lvbi1zdWNjZXNzIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBwYXJlbnRFeHRlbnNpb24gPSBuZXcKICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgIGV4dGVuc2lvbk5hbWUgPSAicGFyZW50LWRhdGEtZXh0ZW5zaW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgc291cmNlID0gInBhcmVudC13b3JrZmxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgIGxvYWRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LAogICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gbmV3CiAgICAgICAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmVudENvbmZpZyA9IG5ldwogICAgICAgICAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heFdhaXRUaW1lID0gMzAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHJ5RW5hYmxlZCA9IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm90aWZpY2F0aW9uTGV2ZWwgPSAiaW5mbyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnRNZXRhZGF0YSA9IG5ldwogICAgICAgICAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdvcmtmbG93VHlwZSA9ICJtYWluIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoYXNBY3RpdmVTdWJmbG93ID0gdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHRlbnNpb25WZXJzaW9uID0gIjEuMC4wIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgIFRhZ3MgPSBuZXdbXSB7ICJzdWJmbG93LXRlc3QiLCAicGFyZW50LWV4dGVuc2lvbiIsICJsb25ncG9sbGluZyIgfQogICAgICAgICAgICB9OwogICAgICAgIH0KICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGV4KQogICAgICAgIHsKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAicGFyZW50LWV4dGVuc2lvbi1lcnJvciIsCiAgICAgICAgICAgICAgICBEYXRhID0gbmV3IHsgZXJyb3IgPSBleC5NZXNzYWdlIH0KICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICB9Cn0KCg==" - } - } - } -} \ No newline at end of file diff --git a/core/Extensions/subflow-test/src/ChildDataExtensionMapping.csx b/core/Extensions/subflow-test/src/ChildDataExtensionMapping.csx deleted file mode 100644 index 3fa0dbc..0000000 --- a/core/Extensions/subflow-test/src/ChildDataExtensionMapping.csx +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Threading.Tasks; -using BBT.Workflow.Scripting; -using BBT.Workflow.Definitions; - -/// -/// Child Data Extension Mapping - Provides additional data for child workflow view -/// This extension is used to test longpolling functionality for child workflow views. -/// -public class ChildDataExtensionMapping : 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 = "child-data-extension", - instanceId = context.Instance?.Id, - workflowKey = context.Workflow?.Key, - parentInstanceId = context.Instance?.Data?.parentInstanceId, - requestedAt = DateTime.UtcNow - }; - - httpTask.SetBody(requestBody); - - // Set headers - var headers = new Dictionary - { - ["Content-Type"] = "application/json", - ["X-Extension-Type"] = "child-data", - ["X-Request-Id"] = Guid.NewGuid().ToString() - }; - httpTask.SetHeaders(headers); - - return Task.FromResult(new ScriptResponse()); - } - catch (Exception ex) - { - return Task.FromResult(new ScriptResponse - { - Key = "child-extension-input-error", - Data = new { error = ex.Message } - }); - } - } - - public async Task OutputHandler(ScriptContext context) - { - try - { - return new ScriptResponse - { - Key = "child-extension-success", - Data = new - { - childExtension = new - { - extensionName = "child-data-extension", - source = "child-workflow", - loadedAt = DateTime.UtcNow, - data = new - { - childConfig = new - { - isSubflow = true, - blocksParent = true, - completionRequired = true - }, - childMetadata = new - { - workflowType = "subflow", - parentWorkflow = "subflow-view-test-parent", - extensionVersion = "1.0.0" - } - } - } - }, - Tags = new[] { "subflow-test", "child-extension", "longpolling" } - }; - } - catch (Exception ex) - { - return new ScriptResponse - { - Key = "child-extension-error", - Data = new { error = ex.Message } - }; - } - } -} - diff --git a/core/Extensions/subflow-test/src/GrandchildDataExtensionMapping.csx b/core/Extensions/subflow-test/src/GrandchildDataExtensionMapping.csx deleted file mode 100644 index 7639345..0000000 --- a/core/Extensions/subflow-test/src/GrandchildDataExtensionMapping.csx +++ /dev/null @@ -1,110 +0,0 @@ -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/Extensions/subflow-test/src/ParentDataExtensionMapping.csx b/core/Extensions/subflow-test/src/ParentDataExtensionMapping.csx deleted file mode 100644 index cf73e7b..0000000 --- a/core/Extensions/subflow-test/src/ParentDataExtensionMapping.csx +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Threading.Tasks; -using BBT.Workflow.Scripting; -using BBT.Workflow.Definitions; - -/// -/// Parent Data Extension Mapping - Provides additional data for parent workflow view -/// This extension is used to test longpolling functionality for parent workflow views. -/// -public class ParentDataExtensionMapping : 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 = "parent-data-extension", - instanceId = context.Instance?.Id, - workflowKey = context.Workflow?.Key, - requestedAt = DateTime.UtcNow - }; - - httpTask.SetBody(requestBody); - - // Set headers - var headers = new Dictionary - { - ["Content-Type"] = "application/json", - ["X-Extension-Type"] = "parent-data", - ["X-Request-Id"] = Guid.NewGuid().ToString() - }; - httpTask.SetHeaders(headers); - - return Task.FromResult(new ScriptResponse()); - } - catch (Exception ex) - { - return Task.FromResult(new ScriptResponse - { - Key = "parent-extension-input-error", - Data = new { error = ex.Message } - }); - } - } - - public async Task OutputHandler(ScriptContext context) - { - try - { - return new ScriptResponse - { - Key = "parent-extension-success", - Data = new - { - parentExtension = new - { - extensionName = "parent-data-extension", - source = "parent-workflow", - loadedAt = DateTime.UtcNow, - data = new - { - parentConfig = new - { - maxWaitTime = 300, - retryEnabled = true, - notificationLevel = "info" - }, - parentMetadata = new - { - workflowType = "main", - hasActiveSubflow = true, - extensionVersion = "1.0.0" - } - } - } - }, - Tags = new[] { "subflow-test", "parent-extension", "longpolling" } - }; - } - catch (Exception ex) - { - return new ScriptResponse - { - Key = "parent-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 deleted file mode 100644 index 2f75752..0000000 --- a/core/Tasks/subflow-test/start-busy-subprocess-task.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "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 deleted file mode 100644 index c30af43..0000000 --- a/core/Tasks/subflow-test/trigger-busy-completion-task.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "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": "subflow-view-test-grandchild", - "transitionName": "complete-busy-state", - "body": {} - } - } -} diff --git a/core/Views/subflow-test/child-workflow-view.json b/core/Views/subflow-test/child-workflow-view.json deleted file mode 100644 index 5068929..0000000 --- a/core/Views/subflow-test/child-workflow-view.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "key": "child-workflow-view", - "version": "1.0.0", - "domain": "core", - "flow": "sys-views", - "flowVersion": "1.0.0", - "tags": [ - "subflow-test", - "child-workflow", - "view", - "longpolling-test" - ], - "attributes": { - "type": 1, - "labels": [ - { - "language": "en-US", - "label": "Child Workflow View" - } - ], - "display": "full-page", - "content": "{\"type\":\"form\",\"title\":{\"en-US\":\"Child Workflow - Active\"},\"description\":{\"en-US\":\"This is the child (subflow) workflow view. Complete this workflow to continue the parent.\"},\"fields\":[{\"key\":\"childStatus\",\"type\":\"text\",\"label\":{\"en-US\":\"Child Status\"},\"readonly\":true,\"defaultValue\":\"active\"},{\"key\":\"childInfo\",\"type\":\"info\",\"label\":{\"en-US\":\"Child Info\"},\"content\":{\"en-US\":\"This view includes child extension data for longpolling test.\"}}],\"buttons\":[{\"key\":\"complete\",\"label\":{\"en-US\":\"Complete Child Workflow\"},\"style\":\"success\",\"action\":\"submit\",\"transition\":\"complete-child\"}]}" - } -} diff --git a/core/Views/subflow-test/grandchild-workflow-view.json b/core/Views/subflow-test/grandchild-workflow-view.json deleted file mode 100644 index 68624e9..0000000 --- a/core/Views/subflow-test/grandchild-workflow-view.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "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" - } - ], - "display": "full-page", - "content": "{\"type\":\"form\",\"title\":{\"en-US\":\"Grandchild Workflow - Active (Level 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.\"},\"fields\":[{\"key\":\"grandchildStatus\",\"type\":\"text\",\"label\":{\"en-US\":\"Grandchild Status\"},\"readonly\":true,\"defaultValue\":\"active\"},{\"key\":\"nestingLevel\",\"type\":\"text\",\"label\":{\"en-US\":\"Nesting Level\"},\"readonly\":true,\"defaultValue\":\"3 (Parent > Child > Grandchild)\"},{\"key\":\"grandchildInfo\",\"type\":\"info\",\"label\":{\"en-US\":\"Grandchild Info\"},\"content\":{\"en-US\":\"This view includes grandchild extension data for deep nested longpolling test.\"}}],\"buttons\":[{\"key\":\"complete\",\"label\":{\"en-US\":\"Complete Grandchild Workflow\"},\"style\":\"success\",\"action\":\"submit\",\"transition\":\"complete-grandchild\"}]}" - } -} diff --git a/core/Views/subflow-test/parent-workflow-view.json b/core/Views/subflow-test/parent-workflow-view.json deleted file mode 100644 index 48a3c54..0000000 --- a/core/Views/subflow-test/parent-workflow-view.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "key": "parent-workflow-view", - "version": "1.0.0", - "domain": "core", - "flow": "sys-views", - "flowVersion": "1.0.0", - "tags": [ - "subflow-test", - "parent-workflow", - "view", - "longpolling-test" - ], - "attributes": { - "type": 1, - "labels": [ - { - "language": "en-US", - "label": "Parent Workflow View" - } - ], - "display": "full-page", - "content": "{\"type\":\"form\",\"title\":{\"en-US\":\"Parent Workflow - Waiting for SubFlow\"},\"description\":{\"en-US\":\"This is the parent workflow view. The workflow is waiting for the subflow to complete.\"},\"fields\":[{\"key\":\"status\",\"type\":\"text\",\"label\":{\"en-US\":\"Current Status\"},\"readonly\":true,\"defaultValue\":\"waiting-for-subflow\"},{\"key\":\"parentInfo\",\"type\":\"info\",\"label\":{\"en-US\":\"Parent Info\"},\"content\":{\"en-US\":\"This view includes parent extension data for longpolling test.\"}}],\"buttons\":[{\"key\":\"proceed\",\"label\":{\"en-US\":\"Proceed After SubFlow\"},\"style\":\"primary\",\"action\":\"submit\",\"transition\":\"proceed-after-subflow\"}]}" - } -} diff --git a/core/Workflows/flow-types/.meta/flow-flow-test.diagram.json b/core/Workflows/flow-types/.meta/flow-flow-test.diagram.json new file mode 100644 index 0000000..00a4467 --- /dev/null +++ b/core/Workflows/flow-types/.meta/flow-flow-test.diagram.json @@ -0,0 +1,3 @@ +{ + "nodePos": {} +} \ No newline at end of file diff --git a/core/Workflows/flow-types/.meta/subflow-flow-test.diagram.json b/core/Workflows/flow-types/.meta/subflow-flow-test.diagram.json new file mode 100644 index 0000000..00a4467 --- /dev/null +++ b/core/Workflows/flow-types/.meta/subflow-flow-test.diagram.json @@ -0,0 +1,3 @@ +{ + "nodePos": {} +} \ No newline at end of file diff --git a/core/Workflows/flow-types/flow-flow-test.json b/core/Workflows/flow-types/flow-flow-test.json index 044b72d..f0b899c 100644 --- a/core/Workflows/flow-types/flow-flow-test.json +++ b/core/Workflows/flow-types/flow-flow-test.json @@ -45,7 +45,7 @@ }, "mapping": { "location": "./src/FlowInitMapping.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEZsb3dJbml0TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC5mbG93VHlwZSA9ICJGIjsKICAgICAgICByZXN1bHQuZmxvd0luaXRpYWxpemVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQuaW5pdEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCJGbG93SW5pdE1hcHBpbmcgY29tcGxldGVkIC0gRmxvdyB0eXBlIHRlc3QiKTsKCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } ] @@ -75,7 +75,7 @@ }, "mapping": { "location": "./src/FlowInitMapping.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEZsb3dJbml0TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC5mbG93VHlwZSA9ICJGIjsKICAgICAgICByZXN1bHQuZmxvd0luaXRpYWxpemVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQuaW5pdEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCJGbG93SW5pdE1hcHBpbmcgY29tcGxldGVkIC0gRmxvdyB0eXBlIHRlc3QiKTsKCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } ], @@ -94,7 +94,7 @@ ], "rule": { "location": "./src/AlwaysTrueRule.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBhc3luYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KfQo=" }, "onExecutionTasks": [] } diff --git a/core/Workflows/flow-types/subflow-flow-test.json b/core/Workflows/flow-types/subflow-flow-test.json index 667198f..542baa4 100644 --- a/core/Workflows/flow-types/subflow-flow-test.json +++ b/core/Workflows/flow-types/subflow-flow-test.json @@ -45,7 +45,7 @@ }, "mapping": { "location": "./src/SubFlowInitMapping.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFN1YkZsb3dJbml0TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC5mbG93VHlwZSA9ICJTIjsKICAgICAgICByZXN1bHQuc3ViRmxvd0luaXRpYWxpemVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQuaW5pdEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCJTdWJGbG93SW5pdE1hcHBpbmcgY29tcGxldGVkIC0gU3ViRmxvdyB0eXBlIHRlc3QiKTsKCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } ] @@ -75,7 +75,7 @@ }, "mapping": { "location": "./src/SubFlowInitMapping.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFN1YkZsb3dJbml0TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC5mbG93VHlwZSA9ICJTIjsKICAgICAgICByZXN1bHQuc3ViRmxvd0luaXRpYWxpemVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQuaW5pdEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCJTdWJGbG93SW5pdE1hcHBpbmcgY29tcGxldGVkIC0gU3ViRmxvdyB0eXBlIHRlc3QiKTsKCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } ], @@ -94,7 +94,7 @@ ], "rule": { "location": "./src/AlwaysTrueRule.csx", - "code": "" + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBhc3luYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KfQo=" }, "onExecutionTasks": [] } diff --git a/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json b/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json index 4b990c2..c0dd84c 100644 --- a/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json +++ b/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json @@ -153,6 +153,33 @@ "mapping": { "location": "./src/ParentToChildSubFlowMapping.csx", "code": "dXNpbmcgU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWM7CnVzaW5nIFN5c3RlbS5EeW5hbWljOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFBhcmVudFRvQ2hpbGRTdWJGbG93TWFwcGluZyA6IFNjcmlwdEJhc2UsIElTdWJGbG93TWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIGNoaWxkSW5wdXQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChkYXRhICE9IG51bGwgJiYgSGFzUHJvcGVydHkoZGF0YSwgInBhcmVudERhdGEiKSkKICAgICAgICB7CiAgICAgICAgICAgIGNoaWxkSW5wdXQucGFyZW50RGF0YSA9IGRhdGEucGFyZW50RGF0YTsKICAgICAgICB9CiAgICAgICAgaWYgKGRhdGEgIT0gbnVsbCAmJiBIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpCiAgICAgICAgewogICAgICAgICAgICBjaGlsZElucHV0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIH0KICAgICAgICBMb2dJbmZvcm1hdGlvbigiUGFyZW50VG9DaGlsZFN1YkZsb3dNYXBwaW5nOiBwcmVwYXJlZCBjaGlsZCBpbnB1dCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IGNoaWxkSW5wdXQgfSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBib2R5ID0gY29udGV4dC5Cb2R5IGFzIElEaWN0aW9uYXJ5PHN0cmluZywgb2JqZWN0PjsKICAgICAgICBkeW5hbWljIG1lcmdlZCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgdmFyIHRhcmdldCA9IChJRGljdGlvbmFyeTxzdHJpbmcsIG9iamVjdD4pbWVyZ2VkOwogICAgICAgIHZhciBpbnN0ID0gY29udGV4dC5JbnN0YW5jZS5EYXRhIGFzIElEaWN0aW9uYXJ5PHN0cmluZywgb2JqZWN0PjsKICAgICAgICBpZiAoaW5zdCAhPSBudWxsKQogICAgICAgIHsKICAgICAgICAgICAgZm9yZWFjaCAodmFyIGt2IGluIGluc3QpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIHRhcmdldFtrdi5LZXldID0ga3YuVmFsdWU7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgaWYgKGJvZHkgIT0gbnVsbCkKICAgICAgICB7CiAgICAgICAgICAgIGZvcmVhY2ggKHZhciBrdiBpbiBib2R5KQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICB0YXJnZXRba3YuS2V5XSA9IGt2LlZhbHVlOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHRhcmdldFsiY2hpbGRDb21wbGV0ZWQiXSA9IHRydWU7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlBhcmVudFRvQ2hpbGRTdWJGbG93TWFwcGluZzogbWVyZ2VkIGNoaWxkIG91dHB1dCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IG1lcmdlZCB9KTsKICAgIH0KfQo=" + }, + "overrides": { + "timeout": { + "key": "child-push-timeout", + "target": "child-cancelled", + "versionStrategy": "Minor", + "timer": { + "reset": "OnEntry", + "duration": "PT15M" + } + }, + "states": { + "child-initial": { + "queryRoles": [ + { "role": "morph-idm.maker", "grant": "allow" }, + { "role": "morph-idm.viewer", "grant": "deny" } + ] + } + }, + "transitions": { + "auto-child-to-subflow": { + "roles": [ + { "role": "morph-idm.maker", "grant": "allow" }, + { "role": "morph-idm.viewer", "grant": "deny" } + ] + } + } } }, "onEntries": [], diff --git a/core/Workflows/subflow-test/.meta/busy-subprocess-workflow.diagram.json b/core/Workflows/subflow-test/.meta/busy-subprocess-workflow.diagram.json deleted file mode 100644 index bd62b40..0000000 --- a/core/Workflows/subflow-test/.meta/busy-subprocess-workflow.diagram.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "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 deleted file mode 100644 index ab02bcf..0000000 --- a/core/Workflows/subflow-test/.meta/subflow-view-test-child.diagram.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "nodePos": { - "child-active": { - "x": 100, - "y": 100 - }, - "child-completed": { - "x": 551.6088888888888, - "y": 96.37333333333333 - }, - "__start__": { - "x": -296, - "y": 100 - } - } -} \ No newline at end of file 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 deleted file mode 100644 index dc7fa73..0000000 --- a/core/Workflows/subflow-test/.meta/subflow-view-test-grandchild.diagram.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "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 deleted file mode 100644 index edd1cd1..0000000 --- a/core/Workflows/subflow-test/.meta/subflow-view-test-parent.diagram.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "nodePos": { - "parent-initial": { - "x": 100, - "y": 100 - }, - "waiting-for-subflow": { - "x": 420, - "y": -120 - }, - "parent-after-subflow": { - "x": 780, - "y": 180 - }, - "parent-completed": { - "x": 1360, - "y": 140 - }, - "__start__": { - "x": -296, - "y": 100 - } - } -} \ No newline at end of file diff --git a/core/Workflows/subflow-test/busy-subprocess-workflow.json b/core/Workflows/subflow-test/busy-subprocess-workflow.json deleted file mode 100644 index a3f3c36..0000000 --- a/core/Workflows/subflow-test/busy-subprocess-workflow.json +++ /dev/null @@ -1,163 +0,0 @@ -{ - "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" - } - ], - "functions": [], - "features": [], - "extensions": [], - "sharedTransitions": [], - "startTransition": { - "key": "start-subprocess", - "target": "processing", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Start SubProcess" - } - ], - "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" - } - ], - "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)" - } - ], - "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" - } - ], - "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)" - } - ], - "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" - } - ], - "view": null, - "subFlow": null, - "onEntries": [], - "onExits": [], - "transitions": [] - } - ] - } -} diff --git a/core/Workflows/subflow-test/src/AlwaysTrueRule.csx b/core/Workflows/subflow-test/src/AlwaysTrueRule.csx deleted file mode 100644 index 44e727b..0000000 --- a/core/Workflows/subflow-test/src/AlwaysTrueRule.csx +++ /dev/null @@ -1,14 +0,0 @@ -using System.Threading.Tasks; -using BBT.Workflow.Scripting; - -/// -/// Always True Rule - Used for auto transitions that should always proceed -/// -public class AlwaysTrueRule : IConditionMapping -{ - public async Task Handler(ScriptContext context) - { - return true; - } -} - diff --git a/core/Workflows/subflow-test/src/BusySubprocessStartMapping.csx b/core/Workflows/subflow-test/src/BusySubprocessStartMapping.csx deleted file mode 100644 index 649ab8f..0000000 --- a/core/Workflows/subflow-test/src/BusySubprocessStartMapping.csx +++ /dev/null @@ -1,58 +0,0 @@ -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/ChildCompleteMapping.csx b/core/Workflows/subflow-test/src/ChildCompleteMapping.csx deleted file mode 100644 index 46c9985..0000000 --- a/core/Workflows/subflow-test/src/ChildCompleteMapping.csx +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Threading.Tasks; -using BBT.Workflow.Definitions; -using BBT.Workflow.Scripting; -using BBT.Workflow.Scripting.Functions; - -/// -/// Child Complete Mapping - Marks the child workflow as completed -/// This data will be returned to the parent workflow -/// -public class ChildCompleteMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public async Task OutputHandler(ScriptContext context) - { - try - { - LogInformation("ChildCompleteMapping - Completing child workflow"); - - var inputData = context.Instance?.Data; - - return new ScriptResponse - { - Key = "child-complete-success", - Data = new - { - completedAt = DateTime.UtcNow, - status = "completed", - result = new - { - childWorkflowId = context.Workflow?.Key, - childInstanceId = context.Instance?.Id, - success = true, - message = "Child workflow completed successfully", - dataForParent = new - { - childResult = "processed", - processingTime = 100, - viewTestPassed = true, - extensionTestPassed = true - } - } - }, - Tags = new[] { "subflow-test", "child-workflow", "completed" } - }; - } - catch (Exception ex) - { - LogError("ChildCompleteMapping - Error: {0}", args: new object?[] { ex.Message }); - return new ScriptResponse - { - Key = "child-complete-error", - Data = new { error = ex.Message } - }; - } - } -} - diff --git a/core/Workflows/subflow-test/src/ChildSharedUpdateMapping.csx b/core/Workflows/subflow-test/src/ChildSharedUpdateMapping.csx deleted file mode 100644 index d93f1eb..0000000 --- a/core/Workflows/subflow-test/src/ChildSharedUpdateMapping.csx +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Threading.Tasks; -using BBT.Workflow.Definitions; -using BBT.Workflow.Scripting; -using BBT.Workflow.Scripting.Functions; - -/// -/// Child Shared Update Mapping - Processes data sent via child's shared transition (target $self). -/// Used to verify issue #425: child (subflow) can handle shared transitions while in its own subflow. -/// -public class ChildSharedUpdateMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public async Task OutputHandler(ScriptContext context) - { - try - { - LogInformation("ChildSharedUpdateMapping - Processing shared transition data on child (target $self)"); - - var body = context.Body as System.Collections.Generic.IDictionary; - object m, v; - var message = body != null && body.TryGetValue("message", out m) ? m?.ToString() : null; - var value = body != null && body.TryGetValue("value", out v) ? v : null; - - return new ScriptResponse - { - Key = "child-shared-update-processed", - Data = new - { - sharedUpdateFromChild = new - { - receivedAt = DateTime.UtcNow, - message = message ?? "no message", - value = value, - workflowKey = context.Workflow?.Key, - instanceId = context.Instance?.Id, - note = "Child flow processed this while in subflow (issue #425 test)" - } - }, - Tags = new[] { "subflow-test", "shared-transition", "child", "issue-425" } - }; - } - catch (Exception ex) - { - LogError("ChildSharedUpdateMapping - Error: {0}", args: new object?[] { ex.Message }); - return new ScriptResponse - { - Key = "child-shared-update-error", - Data = new { error = ex.Message } - }; - } - } -} diff --git a/core/Workflows/subflow-test/src/ChildStartMapping.csx b/core/Workflows/subflow-test/src/ChildStartMapping.csx deleted file mode 100644 index 3fe0801..0000000 --- a/core/Workflows/subflow-test/src/ChildStartMapping.csx +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Threading.Tasks; -using BBT.Workflow.Definitions; -using BBT.Workflow.Scripting; -using BBT.Workflow.Scripting.Functions; - -/// -/// Child Start Mapping - Initializes the child workflow with data from parent -/// -public class ChildStartMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public async Task OutputHandler(ScriptContext context) - { - try - { - LogInformation("ChildStartMapping - Initializing child workflow"); - - var inputData = context.Instance?.Data; - - return new ScriptResponse - { - Key = "child-start-success", - Data = new - { - childWorkflowId = context.Workflow?.Key, - childInstanceId = context.Instance?.Id, - parentInstanceId = inputData?.parentInstanceId, - parentWorkflowId = inputData?.parentWorkflowId, - startedAt = DateTime.UtcNow, - status = "active", - message = "Child workflow started successfully", - childContext = new - { - isSubflow = true, - blocksParent = true, - viewTestEnabled = true, - extensionTestEnabled = true - } - }, - Tags = new[] { "subflow-test", "child-workflow", "started" } - }; - } - catch (Exception ex) - { - LogError("ChildStartMapping - Error: {0}", args: new object?[] { ex.Message }); - return new ScriptResponse - { - Key = "child-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 deleted file mode 100644 index dae94b2..0000000 --- a/core/Workflows/subflow-test/src/ChildToGrandchildSubFlowMapping.csx +++ /dev/null @@ -1,117 +0,0 @@ -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 deleted file mode 100644 index b65ae9c..0000000 --- a/core/Workflows/subflow-test/src/GrandchildCompleteMapping.csx +++ /dev/null @@ -1,63 +0,0 @@ -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 deleted file mode 100644 index 2f97b62..0000000 --- a/core/Workflows/subflow-test/src/GrandchildStartMapping.csx +++ /dev/null @@ -1,64 +0,0 @@ -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/ParentCompleteMapping.csx b/core/Workflows/subflow-test/src/ParentCompleteMapping.csx deleted file mode 100644 index e6ac997..0000000 --- a/core/Workflows/subflow-test/src/ParentCompleteMapping.csx +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Threading.Tasks; -using BBT.Workflow.Definitions; -using BBT.Workflow.Scripting; -using BBT.Workflow.Scripting.Functions; - -/// -/// Parent Complete Mapping - Finalizes the parent workflow -/// -public class ParentCompleteMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public async Task OutputHandler(ScriptContext context) - { - try - { - LogInformation("ParentCompleteMapping - Completing parent workflow"); - - var instanceData = context.Instance?.Data; - - return new ScriptResponse - { - Key = "parent-complete-success", - Data = new - { - completedAt = DateTime.UtcNow, - status = "completed", - testResults = new - { - parentViewTest = "passed", - parentExtensionTest = "passed", - childViewTest = "passed", - childExtensionTest = "passed", - subflowIntegration = "passed", - longpollingTest = "passed" - }, - summary = new - { - parentWorkflowId = context.Workflow?.Key, - parentInstanceId = context.Instance?.Id, - subflowCompleted = instanceData?.subflowCompleted ?? true, - message = "Parent workflow completed successfully with all tests passed" - } - }, - Tags = new[] { "subflow-test", "parent-workflow", "completed", "all-tests-passed" } - }; - } - catch (Exception ex) - { - LogError("ParentCompleteMapping - Error: {0}", args: new object?[] { ex.Message }); - return new ScriptResponse - { - Key = "parent-complete-error", - Data = new { error = ex.Message } - }; - } - } -} - diff --git a/core/Workflows/subflow-test/src/ParentInitialGetInstanceDataMapping.csx b/core/Workflows/subflow-test/src/ParentInitialGetInstanceDataMapping.csx deleted file mode 100644 index f8e42f8..0000000 --- a/core/Workflows/subflow-test/src/ParentInitialGetInstanceDataMapping.csx +++ /dev/null @@ -1,31 +0,0 @@ -using System.Threading.Tasks; -using BBT.Workflow.Scripting; -using BBT.Workflow.Definitions; - -/// -/// Parent Initial - Get Instance Data Mapping -/// Fetches instance data from account-opening workflow on entry of parent-initial state. -/// -public class ParentInitialGetInstanceDataMapping : IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - var triggerTask = (task as GetInstanceDataTask)!; - - triggerTask.SetInstance("50044086189"); - - return Task.FromResult(new ScriptResponse()); - } - - public Task OutputHandler(ScriptContext context) - { - return Task.FromResult(new ScriptResponse() - { - Data = new - { - accountOpeningSnapshot = context.Body, - fetchedAt = System.DateTime.UtcNow - } - }); - } -} diff --git a/core/Workflows/subflow-test/src/ParentSharedUpdateMapping.csx b/core/Workflows/subflow-test/src/ParentSharedUpdateMapping.csx deleted file mode 100644 index e03c2ef..0000000 --- a/core/Workflows/subflow-test/src/ParentSharedUpdateMapping.csx +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Threading.Tasks; -using BBT.Workflow.Definitions; -using BBT.Workflow.Scripting; -using BBT.Workflow.Scripting.Functions; - -/// -/// Parent Shared Update Mapping - Processes data sent via parent's shared transition (target $self). -/// Used to verify issue #425: parent flow can handle shared transitions while instance is in subflow. -/// -public class ParentSharedUpdateMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public async Task OutputHandler(ScriptContext context) - { - try - { - LogInformation("ParentSharedUpdateMapping - Processing shared transition data on parent (target $self)"); - - // Transition body is the PATCH request body (e.g. { "message": "...", "value": ... }) - var body = context.Body as System.Collections.Generic.IDictionary; - object m, v; - var message = body != null && body.TryGetValue("message", out m) ? m?.ToString() : null; - var value = body != null && body.TryGetValue("value", out v) ? v : null; - - return new ScriptResponse - { - Key = "parent-shared-update-processed", - Data = new - { - sharedUpdateFromParent = new - { - receivedAt = DateTime.UtcNow, - message = message ?? "no message", - value = value, - workflowKey = context.Workflow?.Key, - instanceId = context.Instance?.Id, - note = "Parent flow processed this while in subflow (issue #425 test)" - } - }, - Tags = new[] { "subflow-test", "shared-transition", "parent", "issue-425" } - }; - } - catch (Exception ex) - { - LogError("ParentSharedUpdateMapping - Error: {0}", args: new object?[] { ex.Message }); - return new ScriptResponse - { - Key = "parent-shared-update-error", - Data = new { error = ex.Message } - }; - } - } -} diff --git a/core/Workflows/subflow-test/src/ParentStartMapping.csx b/core/Workflows/subflow-test/src/ParentStartMapping.csx deleted file mode 100644 index b9f352d..0000000 --- a/core/Workflows/subflow-test/src/ParentStartMapping.csx +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Threading.Tasks; -using BBT.Workflow.Definitions; -using BBT.Workflow.Scripting; -using BBT.Workflow.Scripting.Functions; - -/// -/// Parent Start Mapping - Initializes the parent workflow -/// -public class ParentStartMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public async Task OutputHandler(ScriptContext context) - { - try - { - LogInformation("ParentStartMapping - Initializing parent workflow"); - - var inputData = context.Instance?.Data; - - return new ScriptResponse - { - Key = "parent-start-success", - Data = new - { - parentWorkflowId = context.Workflow?.Key, - parentInstanceId = context.Instance?.Id, - startedAt = DateTime.UtcNow, - status = "started", - message = "Parent workflow started successfully", - testContext = new - { - testType = "subflow-view-extension-test", - viewTestEnabled = true, - extensionTestEnabled = true, - longpollingTestEnabled = true - }, - parentContext = new - { - isMainWorkflow = true, - willCallSubflow = true, - hasOwnViewAndExtension = true - } - }, - Tags = new[] { "subflow-test", "parent-workflow", "started" } - }; - } - catch (Exception ex) - { - LogError("ParentStartMapping - Error: {0}", args: new object?[] { ex.Message }); - return new ScriptResponse - { - Key = "parent-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 deleted file mode 100644 index 0ec35c7..0000000 --- a/core/Workflows/subflow-test/src/StartBusySubprocessMapping.csx +++ /dev/null @@ -1,89 +0,0 @@ -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/SubFlowMapping.csx b/core/Workflows/subflow-test/src/SubFlowMapping.csx deleted file mode 100644 index 3e3c9ab..0000000 --- a/core/Workflows/subflow-test/src/SubFlowMapping.csx +++ /dev/null @@ -1,111 +0,0 @@ -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; - -/// -/// SubFlow Mapping - Handles input/output for subflow state -/// This mapping prepares data for subflow and processes its result -/// -public class SubFlowMapping : ScriptBase, ISubFlowMapping -{ - /// - /// Prepares input data for the subflow - /// - public async Task InputHandler(ScriptContext context) - { - try - { - LogInformation("SubFlowMapping - Preparing subflow input data"); - - // Prepare data to pass to child workflow using Dictionary - var testContext = new Dictionary - { - ["testType"] = "view-extension-longpolling", - ["expectViewData"] = true, - ["expectExtensionData"] = true - }; - - var inputData = new Dictionary - { - ["parentInstanceId"] = context.Instance?.Id, - ["parentWorkflowId"] = context.Workflow?.Key ?? string.Empty, - ["initiatedAt"] = DateTime.UtcNow, - ["initiatedBy"] = "parent-workflow", - ["testContext"] = testContext - }; - - return new ScriptResponse - { - Key = context.Instance?.Key, - Data = inputData, - Tags = new[] { "subflow-test", "subflow-input" } - }; - } - catch (Exception ex) - { - LogError("SubFlowMapping InputHandler - Error: {0}", args: new object?[] { ex.Message }); - return new ScriptResponse - { - Key = "subflow-input-error", - Data = new Dictionary { ["error"] = ex.Message } - }; - } - } - - /// - /// Processes the result from the completed subflow - /// - public async Task OutputHandler(ScriptContext context) - { - try - { - LogInformation("SubFlowMapping - Processing subflow output data"); - - // Get subflow result data - var subflowResult = context.Body; - - var subflowResultData = new Dictionary - { - ["childInstanceData"] = subflowResult, - ["processed"] = true, - ["viewTestResult"] = "success", - ["extensionTestResult"] = "success" - }; - - var parentContinuation = new Dictionary - { - ["canProceed"] = true, - ["nextState"] = "parent-after-subflow" - }; - - var outputData = new Dictionary - { - ["subflowCompleted"] = true, - ["completedAt"] = DateTime.UtcNow, - ["subflowResult"] = subflowResultData, - ["parentContinuation"] = parentContinuation - }; - - return new ScriptResponse - { - Key = "subflow-output-processed", - Data = outputData, - Tags = new[] { "subflow-test", "subflow-output", "completed" } - }; - } - catch (Exception ex) - { - LogError("SubFlowMapping OutputHandler - Error: {0}", args: new object?[] { ex.Message }); - return new ScriptResponse - { - Key = "subflow-output-error", - Data = new Dictionary { ["error"] = ex.Message } - }; - } - } -} - diff --git a/core/Workflows/subflow-test/src/TriggerParentBusyCompletionMapping.csx b/core/Workflows/subflow-test/src/TriggerParentBusyCompletionMapping.csx deleted file mode 100644 index 0dfb3f1..0000000 --- a/core/Workflows/subflow-test/src/TriggerParentBusyCompletionMapping.csx +++ /dev/null @@ -1,121 +0,0 @@ -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 deleted file mode 100644 index ec889d7..0000000 --- a/core/Workflows/subflow-test/subflow-view-test-child.json +++ /dev/null @@ -1,364 +0,0 @@ -{ - "key": "subflow-view-test-child", - "flow": "sys-flows", - "flowVersion": "1.0.0", - "domain": "core", - "version": "1.0.0", - "tags": [ - "subflow-test", - "child-workflow", - "view-test", - "extension-test", - "longpolling-test", - "nested-subflow" - ], - "attributes": { - "type": "S", - "timeout": { - "key": "push-timeout", - "target": "cancelled", - "versionStrategy": "Minor", - "timer": { - "reset": "OnEntry", - "duration": "PT10M" - } - }, - "labels": [ - { - "language": "en-US", - "label": "SubFlow View Test - Child Workflow" - } - ], - "cancel": { - "key": "cancel", - "target": "cancelled", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Cancel Child Workflow" - } - ], - "onExecutionTasks": [], - "availableIn": [] - }, - "functions": [], - "features": [], - "extensions": [ - { - "key": "child-data-extension", - "domain": "core", - "version": "1.0.0", - "flow": "sys-extensions" - } - ], - "sharedTransitions": [ - { - "key": "child-shared-update", - "target": "$self", - "triggerType": 0, - "versionStrategy": "Minor", - "availableIn": [ - "waiting-for-grandchild" - ], - "labels": [ - { - "language": "en-US", - "label": "Child Shared Update (issue #425)" - } - ], - "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/ChildSharedUpdateMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gQ2hpbGQgU2hhcmVkIFVwZGF0ZSBNYXBwaW5nIC0gUHJvY2Vzc2VzIGRhdGEgc2VudCB2aWEgY2hpbGQncyBzaGFyZWQgdHJhbnNpdGlvbiAodGFyZ2V0ICRzZWxmKS4KLy8vIFVzZWQgdG8gdmVyaWZ5IGlzc3VlICM0MjU6IGNoaWxkIChzdWJmbG93KSBjYW4gaGFuZGxlIHNoYXJlZCB0cmFuc2l0aW9ucyB3aGlsZSBpbiBpdHMgb3duIHN1YmZsb3cuCi8vLyA8L3N1bW1hcnk+CnB1YmxpYyBjbGFzcyBDaGlsZFNoYXJlZFVwZGF0ZU1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIkNoaWxkU2hhcmVkVXBkYXRlTWFwcGluZyAtIFByb2Nlc3Npbmcgc2hhcmVkIHRyYW5zaXRpb24gZGF0YSBvbiBjaGlsZCAodGFyZ2V0ICRzZWxmKSIpOwoKICAgICAgICAgICAgdmFyIGJvZHkgPSBjb250ZXh0LkJvZHkgYXMgU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWMuSURpY3Rpb25hcnk8c3RyaW5nLCBvYmplY3Q+OwogICAgICAgICAgICBvYmplY3QgbSwgdjsKICAgICAgICAgICAgdmFyIG1lc3NhZ2UgPSBib2R5ICE9IG51bGwgJiYgYm9keS5UcnlHZXRWYWx1ZSgibWVzc2FnZSIsIG91dCBtKSA/IG0/LlRvU3RyaW5nKCkgOiBudWxsOwogICAgICAgICAgICB2YXIgdmFsdWUgPSBib2R5ICE9IG51bGwgJiYgYm9keS5UcnlHZXRWYWx1ZSgidmFsdWUiLCBvdXQgdikgPyB2IDogbnVsbDsKCiAgICAgICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gImNoaWxkLXNoYXJlZC11cGRhdGUtcHJvY2Vzc2VkIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBzaGFyZWRVcGRhdGVGcm9tQ2hpbGQgPSBuZXcKICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgIHJlY2VpdmVkQXQgPSBEYXRlVGltZS5VdGNOb3csCiAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBtZXNzYWdlID8/ICJubyBtZXNzYWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSB2YWx1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgd29ya2Zsb3dLZXkgPSBjb250ZXh0LldvcmtmbG93Py5LZXksCiAgICAgICAgICAgICAgICAgICAgICAgIGluc3RhbmNlSWQgPSBjb250ZXh0Lkluc3RhbmNlPy5JZCwKICAgICAgICAgICAgICAgICAgICAgICAgbm90ZSA9ICJDaGlsZCBmbG93IHByb2Nlc3NlZCB0aGlzIHdoaWxlIGluIHN1YmZsb3cgKGlzc3VlICM0MjUgdGVzdCkiCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgIFRhZ3MgPSBuZXdbXSB7ICJzdWJmbG93LXRlc3QiLCAic2hhcmVkLXRyYW5zaXRpb24iLCAiY2hpbGQiLCAiaXNzdWUtNDI1IiB9CiAgICAgICAgICAgIH07CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICBMb2dFcnJvcigiQ2hpbGRTaGFyZWRVcGRhdGVNYXBwaW5nIC0gRXJyb3I6IHswfSIsIGFyZ3M6IG5ldyBvYmplY3Q/W10geyBleC5NZXNzYWdlIH0pOwogICAgICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9ICJjaGlsZC1zaGFyZWQtdXBkYXRlLWVycm9yIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcgeyBlcnJvciA9IGV4Lk1lc3NhZ2UgfQogICAgICAgICAgICB9OwogICAgICAgIH0KICAgIH0KfQo=" - } - } - ] - } - ], - "startTransition": { - "key": "start-child", - "target": "child-initial", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Start Child Workflow" - } - ], - "onExecutionTasks": [ - { - "order": 1, - "task": { - "key": "script-task", - "domain": "core", - "version": "1.0.0", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/ChildStartMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gQ2hpbGQgU3RhcnQgTWFwcGluZyAtIEluaXRpYWxpemVzIHRoZSBjaGlsZCB3b3JrZmxvdyB3aXRoIGRhdGEgZnJvbSBwYXJlbnQKLy8vIDwvc3VtbWFyeT4KcHVibGljIGNsYXNzIENoaWxkU3RhcnRNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBhc3luYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJDaGlsZFN0YXJ0TWFwcGluZyAtIEluaXRpYWxpemluZyBjaGlsZCB3b3JrZmxvdyIpOwogICAgICAgICAgICAKICAgICAgICAgICAgdmFyIGlucHV0RGF0YSA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGE7CiAgICAgICAgICAgIAogICAgICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9ICJjaGlsZC1zdGFydC1zdWNjZXNzIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBjaGlsZFdvcmtmbG93SWQgPSBjb250ZXh0LldvcmtmbG93Py5LZXksCiAgICAgICAgICAgICAgICAgICAgY2hpbGRJbnN0YW5jZUlkID0gY29udGV4dC5JbnN0YW5jZT8uSWQsCiAgICAgICAgICAgICAgICAgICAgcGFyZW50SW5zdGFuY2VJZCA9IGlucHV0RGF0YT8ucGFyZW50SW5zdGFuY2VJZCwKICAgICAgICAgICAgICAgICAgICBwYXJlbnRXb3JrZmxvd0lkID0gaW5wdXREYXRhPy5wYXJlbnRXb3JrZmxvd0lkLAogICAgICAgICAgICAgICAgICAgIHN0YXJ0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdywKICAgICAgICAgICAgICAgICAgICBzdGF0dXMgPSAiYWN0aXZlIiwKICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gIkNoaWxkIHdvcmtmbG93IHN0YXJ0ZWQgc3VjY2Vzc2Z1bGx5IiwKICAgICAgICAgICAgICAgICAgICBjaGlsZENvbnRleHQgPSBuZXcKICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgIGlzU3ViZmxvdyA9IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgIGJsb2Nrc1BhcmVudCA9IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgIHZpZXdUZXN0RW5hYmxlZCA9IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgIGV4dGVuc2lvblRlc3RFbmFibGVkID0gdHJ1ZQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICBUYWdzID0gbmV3W10geyAic3ViZmxvdy10ZXN0IiwgImNoaWxkLXdvcmtmbG93IiwgInN0YXJ0ZWQiIH0KICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCJDaGlsZFN0YXJ0TWFwcGluZyAtIEVycm9yOiB7MH0iLCBhcmdzOiBuZXcgb2JqZWN0P1tdIHsgZXguTWVzc2FnZSB9KTsKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAiY2hpbGQtc3RhcnQtZXJyb3IiLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldyB7IGVycm9yID0gZXguTWVzc2FnZSB9CiAgICAgICAgICAgIH07CiAgICAgICAgfQogICAgfQp9Cgo=" - } - } - ] - }, - "states": [ - { - "key": "child-initial", - "stateType": 1, - "subType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Child Initial State" - } - ], - "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" - } - ], - "roles": [ - { - "role": "morph-idm.viewer", - "grant": "deny" - } - ], - "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" - } - ], - "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==" - }, - "overrides": { - "states": { - "grandchild-active": { - "queryRoles": [ - { - "role": "morph-idm.maker", - "grant": "deny" - }, - { - "role": "morph-idm.viewer", - "grant": "deny" - }, - { - "role": "morph-idm.full", - "grant": "allow" - }, - { - "role": "morph-idm.editor", - "grant": "allow" - } - ] - } - }, - "transitions": { - "enter-busy-state": { - "roles": [ - { - "role": "$InstanceStarter", - "grant": "allow" - }, - { - "role": "morph-idm.viewer", - "grant": "deny" - }, - { - "role": "morph-idm.full", - "grant": "allow" - }, - { - "role": "morph-idm.maker", - "grant": "deny" - } - ] - } - } - } - }, - "onEntries": [], - "onExits": [], - "transitions": [ - { - "key": "grandchild-subflow-completed", - "target": "child-after-grandchild", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Grandchild SubFlow Completed" - } - ], - "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" - } - ], - "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": "complete-child", - "target": "child-completed", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Complete Child Workflow" - } - ], - "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/ChildCompleteMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gQ2hpbGQgQ29tcGxldGUgTWFwcGluZyAtIE1hcmtzIHRoZSBjaGlsZCB3b3JrZmxvdyBhcyBjb21wbGV0ZWQKLy8vIFRoaXMgZGF0YSB3aWxsIGJlIHJldHVybmVkIHRvIHRoZSBwYXJlbnQgd29ya2Zsb3cKLy8vIDwvc3VtbWFyeT4KcHVibGljIGNsYXNzIENoaWxkQ29tcGxldGVNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBhc3luYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJDaGlsZENvbXBsZXRlTWFwcGluZyAtIENvbXBsZXRpbmcgY2hpbGQgd29ya2Zsb3ciKTsKICAgICAgICAgICAgCiAgICAgICAgICAgIHZhciBpbnB1dERhdGEgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhOwogICAgICAgICAgICAKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAiY2hpbGQtY29tcGxldGUtc3VjY2VzcyIsCiAgICAgICAgICAgICAgICBEYXRhID0gbmV3CiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgY29tcGxldGVkQXQgPSBEYXRlVGltZS5VdGNOb3csCiAgICAgICAgICAgICAgICAgICAgc3RhdHVzID0gImNvbXBsZXRlZCIsCiAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gbmV3CiAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICBjaGlsZFdvcmtmbG93SWQgPSBjb250ZXh0LldvcmtmbG93Py5LZXksCiAgICAgICAgICAgICAgICAgICAgICAgIGNoaWxkSW5zdGFuY2VJZCA9IGNvbnRleHQuSW5zdGFuY2U/LklkLAogICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzID0gdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9ICJDaGlsZCB3b3JrZmxvdyBjb21wbGV0ZWQgc3VjY2Vzc2Z1bGx5IiwKICAgICAgICAgICAgICAgICAgICAgICAgZGF0YUZvclBhcmVudCA9IG5ldwogICAgICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGlsZFJlc3VsdCA9ICJwcm9jZXNzZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvY2Vzc2luZ1RpbWUgPSAxMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2aWV3VGVzdFBhc3NlZCA9IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHRlbnNpb25UZXN0UGFzc2VkID0gdHJ1ZQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgIFRhZ3MgPSBuZXdbXSB7ICJzdWJmbG93LXRlc3QiLCAiY2hpbGQtd29ya2Zsb3ciLCAiY29tcGxldGVkIiB9CiAgICAgICAgICAgIH07CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICBMb2dFcnJvcigiQ2hpbGRDb21wbGV0ZU1hcHBpbmcgLSBFcnJvcjogezB9IiwgYXJnczogbmV3IG9iamVjdD9bXSB7IGV4Lk1lc3NhZ2UgfSk7CiAgICAgICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gImNoaWxkLWNvbXBsZXRlLWVycm9yIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcgeyBlcnJvciA9IGV4Lk1lc3NhZ2UgfQogICAgICAgICAgICB9OwogICAgICAgIH0KICAgIH0KfQoK" - } - } - ] - } - ] - }, - { - "key": "child-completed", - "stateType": 3, - "subType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Child Workflow Completed" - } - ], - "view": null, - "subFlow": null, - "onEntries": [], - "onExits": [], - "transitions": [] - }, - { - "key": "cancelled", - "stateType": 3, - "subType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Child Workflow Cancelled" - } - ], - "view": null, - "subFlow": null, - "onEntries": [], - "onExits": [], - "transitions": [] - } - ] - } -} diff --git a/core/Workflows/subflow-test/subflow-view-test-grandchild.json b/core/Workflows/subflow-test/subflow-view-test-grandchild.json deleted file mode 100644 index 6495245..0000000 --- a/core/Workflows/subflow-test/subflow-view-test-grandchild.json +++ /dev/null @@ -1,251 +0,0 @@ -{ - "key": "subflow-view-test-grandchild", - "flow": "sys-flows", - "flowVersion": "1.0.0", - "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" - } - ], - "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" - } - ], - "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" - } - ], - "view": { - "view": { - "key": "grandchild-workflow-view", - "domain": "core", - "version": "1.0.0", - "flow": "sys-views" - }, - "loadData": true, - "extensions": [ - "grandchild-data-extension" - ] - }, - "queryRoles": [ - { - "role": "morph-idm.maker", - "grant": "deny" - }, - { - "role": "morph-idm.viewer", - "grant": "allow" - } - ], - "subFlow": null, - "onEntries": [], - "onExits": [], - "transitions": [ - { - "key": "enter-busy-state", - "target": "grandchild-busy", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Enter Busy State" - } - ], - "roles": [ - { - "role": "morph-idm.maker", - "grant": "allow" - }, - { - "role": "morph-idm.viewer", - "grant": "deny" - }, - { - "role": "morph-idm.full", - "grant": "allow" - } - ], - "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" - } - ], - "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)" - } - ], - "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" - } - ], - "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" - } - ], - "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" - } - ], - "view": null, - "subFlow": null, - "onEntries": [], - "onExits": [], - "transitions": [] - } - ] - } -} diff --git a/core/Workflows/subflow-test/subflow-view-test-parent.json b/core/Workflows/subflow-test/subflow-view-test-parent.json deleted file mode 100644 index 1b2e1bc..0000000 --- a/core/Workflows/subflow-test/subflow-view-test-parent.json +++ /dev/null @@ -1,378 +0,0 @@ -{ - "key": "subflow-view-test-parent", - "flow": "sys-flows", - "flowVersion": "1.0.0", - "domain": "core", - "version": "1.0.0", - "tags": [ - "subflow-test", - "parent-workflow", - "view-test", - "extension-test", - "longpolling-test" - ], - "attributes": { - "type": "F", - "timeout": null, - "labels": [ - { - "language": "en-US", - "label": "SubFlow View Test - Parent Workflow" - } - ], - "cancel": { - "key": "cancel", - "target": "cancelled", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Cancel Parent Workflow" - } - ], - "onExecutionTasks": [], - "availableIn": [] - }, - "functions": [], - "features": [], - "extensions": [ - { - "key": "parent-data-extension", - "domain": "core", - "version": "1.0.0", - "flow": "sys-extensions" - } - ], - "sharedTransitions": [ - { - "key": "parent-shared-update", - "target": "$self", - "triggerType": 0, - "versionStrategy": "Minor", - "availableIn": [ - "waiting-for-subflow" - ], - "labels": [ - { - "language": "en-US", - "label": "Parent Shared Update (issue #425)" - } - ], - "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/ParentSharedUpdateMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gUGFyZW50IFNoYXJlZCBVcGRhdGUgTWFwcGluZyAtIFByb2Nlc3NlcyBkYXRhIHNlbnQgdmlhIHBhcmVudCdzIHNoYXJlZCB0cmFuc2l0aW9uICh0YXJnZXQgJHNlbGYpLgovLy8gVXNlZCB0byB2ZXJpZnkgaXNzdWUgIzQyNTogcGFyZW50IGZsb3cgY2FuIGhhbmRsZSBzaGFyZWQgdHJhbnNpdGlvbnMgd2hpbGUgaW5zdGFuY2UgaXMgaW4gc3ViZmxvdy4KLy8vIDwvc3VtbWFyeT4KcHVibGljIGNsYXNzIFBhcmVudFNoYXJlZFVwZGF0ZU1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIlBhcmVudFNoYXJlZFVwZGF0ZU1hcHBpbmcgLSBQcm9jZXNzaW5nIHNoYXJlZCB0cmFuc2l0aW9uIGRhdGEgb24gcGFyZW50ICh0YXJnZXQgJHNlbGYpIik7CgogICAgICAgICAgICAvLyBUcmFuc2l0aW9uIGJvZHkgaXMgdGhlIFBBVENIIHJlcXVlc3QgYm9keSAoZS5nLiB7ICJtZXNzYWdlIjogIi4uLiIsICJ2YWx1ZSI6IC4uLiB9KQogICAgICAgICAgICB2YXIgYm9keSA9IGNvbnRleHQuQm9keSBhcyBTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5JRGljdGlvbmFyeTxzdHJpbmcsIG9iamVjdD47CiAgICAgICAgICAgIG9iamVjdCBtLCB2OwogICAgICAgICAgICB2YXIgbWVzc2FnZSA9IGJvZHkgIT0gbnVsbCAmJiBib2R5LlRyeUdldFZhbHVlKCJtZXNzYWdlIiwgb3V0IG0pID8gbT8uVG9TdHJpbmcoKSA6IG51bGw7CiAgICAgICAgICAgIHZhciB2YWx1ZSA9IGJvZHkgIT0gbnVsbCAmJiBib2R5LlRyeUdldFZhbHVlKCJ2YWx1ZSIsIG91dCB2KSA/IHYgOiBudWxsOwoKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAicGFyZW50LXNoYXJlZC11cGRhdGUtcHJvY2Vzc2VkIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBzaGFyZWRVcGRhdGVGcm9tUGFyZW50ID0gbmV3CiAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICByZWNlaXZlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LAogICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gbWVzc2FnZSA/PyAibm8gbWVzc2FnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gdmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgIHdvcmtmbG93S2V5ID0gY29udGV4dC5Xb3JrZmxvdz8uS2V5LAogICAgICAgICAgICAgICAgICAgICAgICBpbnN0YW5jZUlkID0gY29udGV4dC5JbnN0YW5jZT8uSWQsCiAgICAgICAgICAgICAgICAgICAgICAgIG5vdGUgPSAiUGFyZW50IGZsb3cgcHJvY2Vzc2VkIHRoaXMgd2hpbGUgaW4gc3ViZmxvdyAoaXNzdWUgIzQyNSB0ZXN0KSIKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgVGFncyA9IG5ld1tdIHsgInN1YmZsb3ctdGVzdCIsICJzaGFyZWQtdHJhbnNpdGlvbiIsICJwYXJlbnQiLCAiaXNzdWUtNDI1IiB9CiAgICAgICAgICAgIH07CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICBMb2dFcnJvcigiUGFyZW50U2hhcmVkVXBkYXRlTWFwcGluZyAtIEVycm9yOiB7MH0iLCBhcmdzOiBuZXcgb2JqZWN0P1tdIHsgZXguTWVzc2FnZSB9KTsKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAicGFyZW50LXNoYXJlZC11cGRhdGUtZXJyb3IiLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldyB7IGVycm9yID0gZXguTWVzc2FnZSB9CiAgICAgICAgICAgIH07CiAgICAgICAgfQogICAgfQp9Cg==" - } - } - ] - } - ], - "startTransition": { - "key": "start-parent", - "target": "parent-initial", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Start Parent Workflow" - } - ], - "onExecutionTasks": [ - { - "order": 1, - "task": { - "key": "script-task", - "domain": "core", - "version": "1.0.0", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/ParentStartMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gUGFyZW50IFN0YXJ0IE1hcHBpbmcgLSBJbml0aWFsaXplcyB0aGUgcGFyZW50IHdvcmtmbG93Ci8vLyA8L3N1bW1hcnk+CnB1YmxpYyBjbGFzcyBQYXJlbnRTdGFydE1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIlBhcmVudFN0YXJ0TWFwcGluZyAtIEluaXRpYWxpemluZyBwYXJlbnQgd29ya2Zsb3ciKTsKICAgICAgICAgICAgCiAgICAgICAgICAgIHZhciBpbnB1dERhdGEgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhOwogICAgICAgICAgICAKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAicGFyZW50LXN0YXJ0LXN1Y2Nlc3MiLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldwogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIHBhcmVudFdvcmtmbG93SWQgPSBjb250ZXh0LldvcmtmbG93Py5LZXksCiAgICAgICAgICAgICAgICAgICAgcGFyZW50SW5zdGFuY2VJZCA9IGNvbnRleHQuSW5zdGFuY2U/LklkLAogICAgICAgICAgICAgICAgICAgIHN0YXJ0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdywKICAgICAgICAgICAgICAgICAgICBzdGF0dXMgPSAic3RhcnRlZCIsCiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9ICJQYXJlbnQgd29ya2Zsb3cgc3RhcnRlZCBzdWNjZXNzZnVsbHkiLAogICAgICAgICAgICAgICAgICAgIHRlc3RDb250ZXh0ID0gbmV3CiAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICB0ZXN0VHlwZSA9ICJzdWJmbG93LXZpZXctZXh0ZW5zaW9uLXRlc3QiLAogICAgICAgICAgICAgICAgICAgICAgICB2aWV3VGVzdEVuYWJsZWQgPSB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICBleHRlbnNpb25UZXN0RW5hYmxlZCA9IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgIGxvbmdwb2xsaW5nVGVzdEVuYWJsZWQgPSB0cnVlCiAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICBwYXJlbnRDb250ZXh0ID0gbmV3CiAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICBpc01haW5Xb3JrZmxvdyA9IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgIHdpbGxDYWxsU3ViZmxvdyA9IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgIGhhc093blZpZXdBbmRFeHRlbnNpb24gPSB0cnVlCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgIFRhZ3MgPSBuZXdbXSB7ICJzdWJmbG93LXRlc3QiLCAicGFyZW50LXdvcmtmbG93IiwgInN0YXJ0ZWQiIH0KICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCJQYXJlbnRTdGFydE1hcHBpbmcgLSBFcnJvcjogezB9IiwgYXJnczogbmV3IG9iamVjdD9bXSB7IGV4Lk1lc3NhZ2UgfSk7CiAgICAgICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gInBhcmVudC1zdGFydC1lcnJvciIsCiAgICAgICAgICAgICAgICBEYXRhID0gbmV3IHsgZXJyb3IgPSBleC5NZXNzYWdlIH0KICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICB9Cn0KCg==" - } - } - ] - }, - "states": [ - { - "key": "parent-initial", - "stateType": 1, - "subType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Parent Initial State" - } - ], - "view": { - "view": { - "key": "parent-workflow-view", - "domain": "core", - "version": "1.0.0", - "flow": "sys-views" - }, - "loadData": true, - "extensions": [ - "parent-data-extension" - ] - }, - "subFlow": null, - "onEntries": [ - { - "order": 1, - "task": { - "key": "get-data-from-workflow", - "domain": "core", - "version": "1.0.0", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/ParentInitialGetInstanceDataMapping.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gUGFyZW50IEluaXRpYWwgLSBHZXQgSW5zdGFuY2UgRGF0YSBNYXBwaW5nCi8vLyBGZXRjaGVzIGluc3RhbmNlIGRhdGEgZnJvbSBhY2NvdW50LW9wZW5pbmcgd29ya2Zsb3cgb24gZW50cnkgb2YgcGFyZW50LWluaXRpYWwgc3RhdGUuCi8vLyA8L3N1bW1hcnk+CnB1YmxpYyBjbGFzcyBQYXJlbnRJbml0aWFsR2V0SW5zdGFuY2VEYXRhTWFwcGluZyA6IElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgdHJpZ2dlclRhc2sgPSAodGFzayBhcyBHZXRJbnN0YW5jZURhdGFUYXNrKSE7CgogICAgICAgIHRyaWdnZXJUYXNrLlNldEluc3RhbmNlKCI1MDA0NDA4NjE4OSIpOwoKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKQogICAgICAgIHsKICAgICAgICAgICAgRGF0YSA9IG5ldwogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBhY2NvdW50T3BlbmluZ1NuYXBzaG90ID0gY29udGV4dC5Cb2R5LAogICAgICAgICAgICAgICAgZmV0Y2hlZEF0ID0gU3lzdGVtLkRhdGVUaW1lLlV0Y05vdwogICAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICB9Cn0K" - } - } - ], - "onExits": [], - "queryRoles": [ - { - "role": "morph-idm.maker", - "grant": "allow" - }, - { - "role": "morph-idm.viewer", - "grant": "allow" - } - ], - "transitions": [ - { - "key": "start-subflow", - "target": "waiting-for-subflow", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Start SubFlow" - } - ], - "schema": null, - "rule": null, - "timer": null, - "view": null, - "onExecutionTasks": [] - } - ] - }, - { - "key": "waiting-for-subflow", - "stateType": 4, - "subType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Waiting for SubFlow" - } - ], - "view": null, - "subFlow": { - "type": "S", - "process": { - "key": "subflow-view-test-child", - "domain": "core", - "version": "1.0.0", - "flow": "sys-flows" - }, - "mapping": { - "location": "./src/SubFlowMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYzsKdXNpbmcgU3lzdGVtLkR5bmFtaWM7CnVzaW5nIFN5c3RlbS5UaHJlYWRpbmcuVGFza3M7CnVzaW5nIEJCVC5Xb3JrZmxvdy5EZWZpbml0aW9uczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZy5GdW5jdGlvbnM7CgovLy8gPHN1bW1hcnk+Ci8vLyBTdWJGbG93IE1hcHBpbmcgLSBIYW5kbGVzIGlucHV0L291dHB1dCBmb3Igc3ViZmxvdyBzdGF0ZQovLy8gVGhpcyBtYXBwaW5nIHByZXBhcmVzIGRhdGEgZm9yIHN1YmZsb3cgYW5kIHByb2Nlc3NlcyBpdHMgcmVzdWx0Ci8vLyA8L3N1bW1hcnk+CnB1YmxpYyBjbGFzcyBTdWJGbG93TWFwcGluZyA6IFNjcmlwdEJhc2UsIElTdWJGbG93TWFwcGluZwp7CiAgICAvLy8gPHN1bW1hcnk+CiAgICAvLy8gUHJlcGFyZXMgaW5wdXQgZGF0YSBmb3IgdGhlIHN1YmZsb3cKICAgIC8vLyA8L3N1bW1hcnk+CiAgICBwdWJsaWMgYXN5bmMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJTdWJGbG93TWFwcGluZyAtIFByZXBhcmluZyBzdWJmbG93IGlucHV0IGRhdGEiKTsKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIFByZXBhcmUgZGF0YSB0byBwYXNzIHRvIGNoaWxkIHdvcmtmbG93IHVzaW5nIERpY3Rpb25hcnkKICAgICAgICAgICAgdmFyIHRlc3RDb250ZXh0ID0gbmV3IERpY3Rpb25hcnk8c3RyaW5nLCBvYmplY3Q+CiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIFsidGVzdFR5cGUiXSA9ICJ2aWV3LWV4dGVuc2lvbi1sb25ncG9sbGluZyIsCiAgICAgICAgICAgICAgICBbImV4cGVjdFZpZXdEYXRhIl0gPSB0cnVlLAogICAgICAgICAgICAgICAgWyJleHBlY3RFeHRlbnNpb25EYXRhIl0gPSB0cnVlCiAgICAgICAgICAgIH07CiAgICAgICAgICAgIAogICAgICAgICAgICB2YXIgaW5wdXREYXRhID0gbmV3IERpY3Rpb25hcnk8c3RyaW5nLCBvYmplY3Q+CiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIFsicGFyZW50SW5zdGFuY2VJZCJdID0gY29udGV4dC5JbnN0YW5jZT8uSWQsCiAgICAgICAgICAgICAgICBbInBhcmVudFdvcmtmbG93SWQiXSA9IGNvbnRleHQuV29ya2Zsb3c/LktleSA/PyBzdHJpbmcuRW1wdHksCiAgICAgICAgICAgICAgICBbImluaXRpYXRlZEF0Il0gPSBEYXRlVGltZS5VdGNOb3csCiAgICAgICAgICAgICAgICBbImluaXRpYXRlZEJ5Il0gPSAicGFyZW50LXdvcmtmbG93IiwKICAgICAgICAgICAgICAgIFsidGVzdENvbnRleHQiXSA9IHRlc3RDb250ZXh0CiAgICAgICAgICAgIH07CiAgICAgICAgICAgIAogICAgICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9IGNvbnRleHQuSW5zdGFuY2U/LktleSwKICAgICAgICAgICAgICAgIERhdGEgPSBpbnB1dERhdGEsCiAgICAgICAgICAgICAgICBUYWdzID0gbmV3W10geyAic3ViZmxvdy10ZXN0IiwgInN1YmZsb3ctaW5wdXQiIH0KICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCJTdWJGbG93TWFwcGluZyBJbnB1dEhhbmRsZXIgLSBFcnJvcjogezB9IiwgYXJnczogbmV3IG9iamVjdD9bXSB7IGV4Lk1lc3NhZ2UgfSk7CiAgICAgICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gInN1YmZsb3ctaW5wdXQtZXJyb3IiLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldyBEaWN0aW9uYXJ5PHN0cmluZywgb2JqZWN0PiB7IFsiZXJyb3IiXSA9IGV4Lk1lc3NhZ2UgfQogICAgICAgICAgICB9OwogICAgICAgIH0KICAgIH0KCiAgICAvLy8gPHN1bW1hcnk+CiAgICAvLy8gUHJvY2Vzc2VzIHRoZSByZXN1bHQgZnJvbSB0aGUgY29tcGxldGVkIHN1YmZsb3cKICAgIC8vLyA8L3N1bW1hcnk+CiAgICBwdWJsaWMgYXN5bmMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdHJ5CiAgICAgICAgewogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiU3ViRmxvd01hcHBpbmcgLSBQcm9jZXNzaW5nIHN1YmZsb3cgb3V0cHV0IGRhdGEiKTsKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIEdldCBzdWJmbG93IHJlc3VsdCBkYXRhCiAgICAgICAgICAgIHZhciBzdWJmbG93UmVzdWx0ID0gY29udGV4dC5Cb2R5OwogICAgICAgICAgICAKICAgICAgICAgICAgdmFyIHN1YmZsb3dSZXN1bHREYXRhID0gbmV3IERpY3Rpb25hcnk8c3RyaW5nLCBvYmplY3Q+CiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIFsiY2hpbGRJbnN0YW5jZURhdGEiXSA9IHN1YmZsb3dSZXN1bHQsCiAgICAgICAgICAgICAgICBbInByb2Nlc3NlZCJdID0gdHJ1ZSwKICAgICAgICAgICAgICAgIFsidmlld1Rlc3RSZXN1bHQiXSA9ICJzdWNjZXNzIiwKICAgICAgICAgICAgICAgIFsiZXh0ZW5zaW9uVGVzdFJlc3VsdCJdID0gInN1Y2Nlc3MiCiAgICAgICAgICAgIH07CiAgICAgICAgICAgIAogICAgICAgICAgICB2YXIgcGFyZW50Q29udGludWF0aW9uID0gbmV3IERpY3Rpb25hcnk8c3RyaW5nLCBvYmplY3Q+CiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIFsiY2FuUHJvY2VlZCJdID0gdHJ1ZSwKICAgICAgICAgICAgICAgIFsibmV4dFN0YXRlIl0gPSAicGFyZW50LWFmdGVyLXN1YmZsb3ciCiAgICAgICAgICAgIH07CiAgICAgICAgICAgIAogICAgICAgICAgICB2YXIgb3V0cHV0RGF0YSA9IG5ldyBEaWN0aW9uYXJ5PHN0cmluZywgb2JqZWN0PgogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBbInN1YmZsb3dDb21wbGV0ZWQiXSA9IHRydWUsCiAgICAgICAgICAgICAgICBbImNvbXBsZXRlZEF0Il0gPSBEYXRlVGltZS5VdGNOb3csCiAgICAgICAgICAgICAgICBbInN1YmZsb3dSZXN1bHQiXSA9IHN1YmZsb3dSZXN1bHREYXRhLAogICAgICAgICAgICAgICAgWyJwYXJlbnRDb250aW51YXRpb24iXSA9IHBhcmVudENvbnRpbnVhdGlvbgogICAgICAgICAgICB9OwogICAgICAgICAgICAKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAic3ViZmxvdy1vdXRwdXQtcHJvY2Vzc2VkIiwKICAgICAgICAgICAgICAgIERhdGEgPSBvdXRwdXREYXRhLAogICAgICAgICAgICAgICAgVGFncyA9IG5ld1tdIHsgInN1YmZsb3ctdGVzdCIsICJzdWJmbG93LW91dHB1dCIsICJjb21wbGV0ZWQiIH0KICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCJTdWJGbG93TWFwcGluZyBPdXRwdXRIYW5kbGVyIC0gRXJyb3I6IHswfSIsIGFyZ3M6IG5ldyBvYmplY3Q/W10geyBleC5NZXNzYWdlIH0pOwogICAgICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9ICJzdWJmbG93LW91dHB1dC1lcnJvciIsCiAgICAgICAgICAgICAgICBEYXRhID0gbmV3IERpY3Rpb25hcnk8c3RyaW5nLCBvYmplY3Q+IHsgWyJlcnJvciJdID0gZXguTWVzc2FnZSB9CiAgICAgICAgICAgIH07CiAgICAgICAgfQogICAgfQp9Cgo=" - }, - "overrides": { - "timeout": { - "key": "push-timeout", - "target": "cancelled", - "versionStrategy": "Minor", - "timer": { - "reset": "OnEntry", - "duration": "PT25M" - } - }, - "transitions": { - "start-grandchild-subflow": { - "roles": [ - { - "role": "morph-idm.maker", - "grant": "allow" - }, - { - "role": "morph-idm.viewer", - "grant": "deny" - } - ] - } - }, - "states": { - "child-initial": { - "queryRoles": [ - { - "role": "morph-idm.maker", - "grant": "allow" - }, - { - "role": "morph-idm.viewer", - "grant": "deny" - } - ] - }, - "waiting-for-grandchild": { - "queryRoles": [ - { - "role": "morph-idm.maker", - "grant": "deny" - }, - { - "role": "morph-idm.viewer", - "grant": "deny" - } - ] - } - } - } - }, - "onEntries": [], - "onExits": [], - "transitions": [ - { - "key": "subflow-completed", - "target": "parent-after-subflow", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "SubFlow Completed" - } - ], - "schema": null, - "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCi8vLyA8c3VtbWFyeT4KLy8vIEFsd2F5cyBUcnVlIFJ1bGUgLSBVc2VkIGZvciBhdXRvIHRyYW5zaXRpb25zIHRoYXQgc2hvdWxkIGFsd2F5cyBwcm9jZWVkCi8vLyA8L3N1bW1hcnk+CnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBhc3luYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiB0cnVlOwogICAgfQp9Cgo=" - }, - "timer": null, - "view": null, - "onExecutionTasks": [] - } - ] - }, - { - "key": "parent-after-subflow", - "stateType": 2, - "subType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "After SubFlow State" - } - ], - "view": { - "view": { - "key": "parent-workflow-view", - "domain": "core", - "version": "1.0.0", - "flow": "sys-views" - }, - "loadData": true, - "extensions": [ - "parent-data-extension" - ] - }, - "subFlow": null, - "onEntries": [], - "onExits": [], - "transitions": [ - { - "key": "proceed-after-subflow", - "target": "parent-completed", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Proceed After SubFlow" - } - ], - "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/ParentCompleteMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gUGFyZW50IENvbXBsZXRlIE1hcHBpbmcgLSBGaW5hbGl6ZXMgdGhlIHBhcmVudCB3b3JrZmxvdwovLy8gPC9zdW1tYXJ5PgpwdWJsaWMgY2xhc3MgUGFyZW50Q29tcGxldGVNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBhc3luYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJQYXJlbnRDb21wbGV0ZU1hcHBpbmcgLSBDb21wbGV0aW5nIHBhcmVudCB3b3JrZmxvdyIpOwogICAgICAgICAgICAKICAgICAgICAgICAgdmFyIGluc3RhbmNlRGF0YSA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGE7CiAgICAgICAgICAgIAogICAgICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9ICJwYXJlbnQtY29tcGxldGUtc3VjY2VzcyIsCiAgICAgICAgICAgICAgICBEYXRhID0gbmV3CiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgY29tcGxldGVkQXQgPSBEYXRlVGltZS5VdGNOb3csCiAgICAgICAgICAgICAgICAgICAgc3RhdHVzID0gImNvbXBsZXRlZCIsCiAgICAgICAgICAgICAgICAgICAgdGVzdFJlc3VsdHMgPSBuZXcKICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgIHBhcmVudFZpZXdUZXN0ID0gInBhc3NlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgIHBhcmVudEV4dGVuc2lvblRlc3QgPSAicGFzc2VkIiwKICAgICAgICAgICAgICAgICAgICAgICAgY2hpbGRWaWV3VGVzdCA9ICJwYXNzZWQiLAogICAgICAgICAgICAgICAgICAgICAgICBjaGlsZEV4dGVuc2lvblRlc3QgPSAicGFzc2VkIiwKICAgICAgICAgICAgICAgICAgICAgICAgc3ViZmxvd0ludGVncmF0aW9uID0gInBhc3NlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgIGxvbmdwb2xsaW5nVGVzdCA9ICJwYXNzZWQiCiAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICBzdW1tYXJ5ID0gbmV3CiAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnRXb3JrZmxvd0lkID0gY29udGV4dC5Xb3JrZmxvdz8uS2V5LAogICAgICAgICAgICAgICAgICAgICAgICBwYXJlbnRJbnN0YW5jZUlkID0gY29udGV4dC5JbnN0YW5jZT8uSWQsCiAgICAgICAgICAgICAgICAgICAgICAgIHN1YmZsb3dDb21wbGV0ZWQgPSBpbnN0YW5jZURhdGE/LnN1YmZsb3dDb21wbGV0ZWQgPz8gdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9ICJQYXJlbnQgd29ya2Zsb3cgY29tcGxldGVkIHN1Y2Nlc3NmdWxseSB3aXRoIGFsbCB0ZXN0cyBwYXNzZWQiCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgIFRhZ3MgPSBuZXdbXSB7ICJzdWJmbG93LXRlc3QiLCAicGFyZW50LXdvcmtmbG93IiwgImNvbXBsZXRlZCIsICJhbGwtdGVzdHMtcGFzc2VkIiB9CiAgICAgICAgICAgIH07CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICBMb2dFcnJvcigiUGFyZW50Q29tcGxldGVNYXBwaW5nIC0gRXJyb3I6IHswfSIsIGFyZ3M6IG5ldyBvYmplY3Q/W10geyBleC5NZXNzYWdlIH0pOwogICAgICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9ICJwYXJlbnQtY29tcGxldGUtZXJyb3IiLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldyB7IGVycm9yID0gZXguTWVzc2FnZSB9CiAgICAgICAgICAgIH07CiAgICAgICAgfQogICAgfQp9Cgo=" - } - } - ] - } - ] - }, - { - "key": "parent-completed", - "stateType": 3, - "subType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Parent Workflow Completed" - } - ], - "view": null, - "subFlow": null, - "onEntries": [], - "onExits": [], - "transitions": [] - }, - { - "key": "cancelled", - "stateType": 3, - "subType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Parent Workflow Cancelled" - } - ], - "view": null, - "subFlow": null, - "onEntries": [], - "onExits": [], - "transitions": [] - } - ] - } -} diff --git a/doc/integration-test-documentation.md b/doc/integration-test-documentation.md index 3e8025e..83c9c30 100644 --- a/doc/integration-test-documentation.md +++ b/doc/integration-test-documentation.md @@ -9,7 +9,7 @@ Bu dokuman, `core` projesindeki tum integration test workflow'larini, icerdikler | # | Grup | Workflow | Test Edilen Ana Ozellikler | |---|------|----------|---------------------------| | 1 | Lifecycle & Transitions | `lifecycle-transitions-test-workflow` | State tipleri, transition tipleri, on-entries/exits, timer, cancel, **exit** (`attributes.exit`), **schedule iptal / timer reschedule**, **queryRoles** ve **transition `roles` (yalnizca state fonksiyonu listeleme filtresi)** | -| 2 | SubFlow & SubProcess | `subflow-orchestration-parent` + child + grandchild | Parent-child-grandchild zinciri, parent shared transitions, **child shared transition**, **cancel cascade** (child/grandchild **cancelled** final state'leri), **effective state**, **updateData (SubFlow)** | +| 2 | SubFlow & SubProcess | `subflow-orchestration-parent` + child + grandchild | Parent-child-grandchild zinciri, parent shared transitions, **child shared transition**, **cancel cascade** (child/grandchild **cancelled** final state'leri), **effective state**, **updateData (SubFlow)**, **subFlow.overrides** (timeout, states.queryRoles, transitions.roles) | | 3 | Task Execution | `task-execution-test-workflow` + `task-target-workflow` + `extended-tasks-test-workflow` | HTTP/Script/StartFlow/GetInstanceData/Notification/TriggerTransition/SubProcess/GetInstances; Dapr HTTP/Service/PubSub (`extended-tasks-test-workflow`); Mocklab + Dapr YAML | | 4 | Error Boundary | `error-boundary-test-workflow` | Retry, Ignore, **Rollback**, **Log**, **Notify** (action:4) aksiyonlari, retry policy, priority, **timeoutPolicy** (onTimeout), **errorHandlerRule** (errorTypes/errorCodes) | | 5 | View, Function & Extension | `view-function-extension-test-workflow` | View tipleri, display modlari, function, extension, wizard state, **features** referansi | @@ -17,6 +17,7 @@ Bu dokuman, `core` projesindeki tum integration test workflow'larini, icerdikler | 7 | Instance Management | `instance-management-test-workflow` | Filtering, pagination, sorting, timeout, idempotent start, **subType 4/5/6** (suspended/busy/human) | | 8 | Flow Types | `core-flow-test` + `subprocess-flow-test` + `flow-flow-test` + `subflow-flow-test` | **Core (C)**, **SubProcess (P)**, **Flow (F)** ve **SubFlow (S)** workflow tipleri | | 9 | Version Consistency | `version-consistency-test-workflow` (v1.0.0 + v2.0.0) | Workflow versiyon degisiminde mevcut instance'larin kendi versiyonlariyla devam etmesi | +| 10 | Dynamic Collection & Object | `collection-object-test-workflow` | ScriptBase dinamik koleksiyon/nesne API'leri (CreateObject, CreateList, SetProperty, GetList, AsList, ListFilter, ListCount, ListAny, ListFirst, ListLast, ListSelect, ListAdd, ListRemove, RemoveProperty, ToDictionary, HasProperty) | **Grup numaralari:** Eski **Grup 8 (Extended Tasks)** icerigi **Grup 3 (Task Execution)** altinda birlestirilmistir (`extended-tasks-test-workflow` artik Grup 3 kapsaminda). Matris ve tablolarda **G8 / Grup 8** = Flow Types, **G9 / Grup 9** = Version Consistency. @@ -186,7 +187,7 @@ lifecycle-transitions-test-workflow (F) - `subflow-orchestration-child` (type: S) - Alt flow - `subflow-orchestration-grandchild` (type: S) - Torun flow -**Amac:** Parent-child-grandchild SubFlow zincirini, parent seviyesindeki shared transition'lari, **child workflow uzerindeki shared transition'u**, **cancel cascade** (parent iptalinde alt akislarda **child-cancelled** / **grandchild-cancelled** final state'leri), **`/functions/state` uzerinden effective state** gorunurlugunu ve **updateData transition** (SubFlow'dan parent data guncelleme) ozelligini test eder. +**Amac:** Parent-child-grandchild SubFlow zincirini, parent seviyesindeki shared transition'lari, **child workflow uzerindeki shared transition'u**, **cancel cascade** (parent iptalinde alt akislarda **child-cancelled** / **grandchild-cancelled** final state'leri), **`/functions/state` uzerinden effective state** gorunurlugunu, **updateData transition** (SubFlow'dan parent data guncelleme) ozelligini ve **`subFlow.overrides`** mekanizmasini (parent'tan child workflow'a timeout, state `queryRoles` ve transition `roles` override etme) test eder. ### Agac Yapisi @@ -213,7 +214,11 @@ subflow-orchestration-parent (F) ├── parent-subflow-state (SubFlow, stateType:4) │ ├── subFlow: type "S" │ │ ├── process: subflow-orchestration-child -│ │ └── mapping: ParentToChildSubFlowMapping.csx (ISubFlowMapping) +│ │ ├── mapping: ParentToChildSubFlowMapping.csx (ISubFlowMapping) +│ │ └── overrides: +│ │ ├── timeout: child-push-timeout → child-cancelled (PT15M, OnEntry) +│ │ ├── states.child-initial.queryRoles: [morph-idm.maker=allow, morph-idm.viewer=deny] +│ │ └── transitions.auto-child-to-subflow.roles: [morph-idm.maker=allow, morph-idm.viewer=deny] │ └── Transitions: │ └── auto-parent-to-completed (Auto) → parent-completed │ └── rule: AlwaysTrueRule.csx @@ -291,6 +296,9 @@ subflow-orchestration-parent (F) | **Effective state** | `/functions/state` ile en derin aktif alt akis durumu (or. grandchild-initial) | HTTP Test 4 | | **UpdateData transition (SubFlow)** | Child'dan parent instance datasini guncelleme ($self) | update-parent-data, UpdateParentDataMapping.csx | | Manuel transition (SubFlow icinde) | Grandchild'da kullanici tetikli tamamlama | complete-grandchild | +| **subFlow.overrides.timeout** | Parent'tan child'a timeout override (PT15M, OnEntry, → child-cancelled) | parent-subflow-state.subFlow.overrides.timeout | +| **subFlow.overrides.states.queryRoles** | Parent'tan child-initial state'inin queryRoles'unu ezme | parent-subflow-state.subFlow.overrides.states.child-initial | +| **subFlow.overrides.transitions.roles** | Parent'tan auto-child-to-subflow transition'inin roles'unu ezme | parent-subflow-state.subFlow.overrides.transitions.auto-child-to-subflow | ### HTTP Test Dosyasi (`subflow-orchestration.http`) @@ -1025,6 +1033,121 @@ version-consistency-test-workflow (F, v2.0.0) — 4 state (ekstra review-state) --- +## Grup 10: Dynamic Collection & Object + +**Workflow:** `collection-object-test-workflow` (type: F) + +**Amac:** `ScriptBase` sinifinin sagladigi dinamik koleksiyon ve nesne yardimci metodlarini gercek bir workflow context'inde uctan uca dogrular. Her state bir API grubunu test eder ve `AlwaysTrueRule` ile bir sonraki state'e otomatik gecer. + +### Agac Yapisi + +``` +collection-object-test-workflow (F) +│ +├── [startTransition] start-collection-test +│ └── onExecutionTasks: +│ └── script-task + InitCollectionTestMapping.csx +│ +├── test-create-and-set-state (Initial, stateType:1) +│ ├── onEntries: +│ │ └── script-task + CreateAndSetMapping.csx +│ │ (CreateObject, CreateList, SetProperty, ListAdd) +│ └── Transitions: +│ └── create-and-set-completed (Auto) → test-get-list-state +│ └── rule: AlwaysTrueRule.csx +│ +├── test-get-list-state (Intermediate, stateType:2) +│ ├── onEntries: +│ │ └── script-task + GetListAndAsListMapping.csx +│ │ (GetList, AsList — null/non-list edge case) +│ └── Transitions: +│ └── get-list-completed (Auto) → test-filter-count-any-state +│ └── rule: AlwaysTrueRule.csx +│ +├── test-filter-count-any-state (Intermediate, stateType:2) +│ ├── onEntries: +│ │ └── script-task + ListFilterCountAnyMapping.csx +│ │ (ListFilter, ListCount, ListAny — predicate'li/predicate'siz, empty list) +│ └── Transitions: +│ └── filter-count-any-completed (Auto) → test-first-last-state +│ └── rule: AlwaysTrueRule.csx +│ +├── test-first-last-state (Intermediate, stateType:2) +│ ├── onEntries: +│ │ └── script-task + ListFirstLastMapping.csx +│ │ (ListFirst, ListLast — predicate, no-match → null, empty list) +│ └── Transitions: +│ └── first-last-completed (Auto) → test-list-select-state +│ └── rule: AlwaysTrueRule.csx +│ +├── test-list-select-state (Intermediate, stateType:2) +│ ├── onEntries: +│ │ └── script-task + ListSelectMapping.csx +│ │ (ListSelect, ListSelect, transformation, empty list) +│ └── Transitions: +│ └── list-select-completed (Auto) → test-add-remove-state +│ └── rule: AlwaysTrueRule.csx +│ +├── test-add-remove-state (Intermediate, stateType:2) +│ ├── onEntries: +│ │ └── script-task + ListAddRemoveMapping.csx +│ │ (ListAdd, ListRemove — predicate ile sayim, count dogrulama) +│ └── Transitions: +│ └── add-remove-completed (Auto) → test-remove-prop-to-dict-state +│ └── rule: AlwaysTrueRule.csx +│ +├── test-remove-prop-to-dict-state (Intermediate, stateType:2) +│ ├── onEntries: +│ │ └── script-task + RemovePropertyToDictionaryMapping.csx +│ │ (RemoveProperty, ToDictionary, HasProperty — null edge case) +│ └── Transitions: +│ └── remove-prop-to-dict-completed (Auto) → test-completed-state +│ └── rule: AlwaysTrueRule.csx +│ +└── test-completed-state (Final/Success, stateType:3, subType:1) +``` + +### Test Edilen Ozellikler + +| Ozellik | Nasil Test Edilir | Ilgili Eleman | +|---------|-------------------|---------------| +| CreateObject() | Yeni dinamik nesne olusturma | CreateAndSetMapping.csx | +| CreateList() | Bos dinamik liste olusturma | CreateAndSetMapping.csx | +| SetProperty(obj, key, val) | Dinamik nesneye property atama | CreateAndSetMapping.csx | +| ListAdd(list, item) | Listeye eleman ekleme | CreateAndSetMapping.csx, ListAddRemoveMapping.csx | +| GetList(obj, propName) | Instance data'dan liste alma | GetListAndAsListMapping.csx | +| AsList(value) | Degeri listeye donusturme (null/non-list edge case) | GetListAndAsListMapping.csx | +| ListFilter(list, predicate) | Kosul ile filtreleme | ListFilterCountAnyMapping.csx | +| ListCount(list) / ListCount(list, predicate) | Predicate'li/predicate'siz sayim | ListFilterCountAnyMapping.csx | +| ListAny(list) / ListAny(list, predicate) | Eleman varlik kontrolu | ListFilterCountAnyMapping.csx | +| ListFirst(list) / ListFirst(list, predicate) | Ilk eleman (no-match → null) | ListFirstLastMapping.csx | +| ListLast(list) / ListLast(list, predicate) | Son eleman (no-match → null) | ListFirstLastMapping.csx | +| ListSelect(list, selector) | Projeksiyon: string, int, transformation | ListSelectMapping.csx | +| ListRemove(list, predicate) | Kosul ile eleman silme | ListAddRemoveMapping.csx | +| RemoveProperty(obj, key) | Property silme (non-existent → false) | RemovePropertyToDictionaryMapping.csx | +| ToDictionary(obj) | ExpandoObject → Dictionary (null → empty) | RemovePropertyToDictionaryMapping.csx | +| HasProperty(obj, key) | Property varlik kontrolu (ScriptBase) | RemovePropertyToDictionaryMapping.csx | +| Empty list / null edge cases | Bos liste ve null ile graceful degradation | GetListAndAsListMapping, ListFilterCountAnyMapping, ListFirstLastMapping, ListSelectMapping | + +### Kullanilan Bilesenler + +| Tip | Anahtar | Dosya | +|-----|---------|-------| +| Task | `script-task` (paylasimli, type:7) | Tasks/lifecycle-transitions/script-task.json | +| CSX | InitCollectionTestMapping | Workflows/collection-object-test/src/InitCollectionTestMapping.csx | +| CSX | CreateAndSetMapping | Workflows/collection-object-test/src/CreateAndSetMapping.csx | +| CSX | GetListAndAsListMapping | Workflows/collection-object-test/src/GetListAndAsListMapping.csx | +| CSX | ListFilterCountAnyMapping | Workflows/collection-object-test/src/ListFilterCountAnyMapping.csx | +| CSX | ListFirstLastMapping | Workflows/collection-object-test/src/ListFirstLastMapping.csx | +| CSX | ListSelectMapping | Workflows/collection-object-test/src/ListSelectMapping.csx | +| CSX | ListAddRemoveMapping | Workflows/collection-object-test/src/ListAddRemoveMapping.csx | +| CSX | RemovePropertyToDictionaryMapping | Workflows/collection-object-test/src/RemovePropertyToDictionaryMapping.csx | +| CSX | AlwaysTrueRule | Workflows/collection-object-test/src/AlwaysTrueRule.csx | + +**HTTP / C# integration test:** Henuz eklenmedi. + +--- + ## Altyapi Bilesenleri ### Docker Compose @@ -1083,83 +1206,89 @@ Integration testlerde kullanilan C# script interface'leri: ## Ozellik Kapsam Matrisi -Her grubun hangi vNext ozelliklerini test ettigini gosteren matris. **Gn** sutunu Genel Bakis tablosundaki **Grup n** ile eslesir (1-9; birlestirilmis Dapr senaryosu **G3**). - -| Ozellik | G1 | G2 | G3 | G4 | G5 | G6 | G7 | G8 | G9 | -|---------|----|----|----|----|----|----|----|----|----| -| State tipleri (1/2/3) | X | X | X | X | X | X | X | X | X | -| State alt tipleri (1/2/3) | X | X | | X | | | X | | | -| **State alt tipleri (4/5/6)** | | | | | | | X | | | -| SubFlow state (4) | | X | | | | | | | | -| Wizard state (5) | | | | | X | | | | | -| Manuel transition (0) | X | X | | | X | X | X | | X | -| Otomatik transition (1) | X | X | X | X | X | | | X | X | -| Zamanlanmis transition (2) | X | | | | | | | | | -| triggerKind:10 (default) | X | | | | X | | | | | -| onEntries | X | X | X | X | | | | X | X | -| onExits | X | | | | | | | | | -| startTransition tasks | X | X | X | X | X | X | X | X | X | -| IMapping | X | X | X | X | X | X | X | X | X | -| IConditionMapping | X | X | X | X | X | | | X | X | -| ITimerMapping | X | | | | | | | | | -| ITransitionMapping | X | | | | | | | | | -| ISubFlowMapping | | X | | | | | | | | -| IOutputHandler | | | | | X | | | | | -| HTTP Task (type:6) | | | X | X | | | | | | -| Script Task (type:7) | X | X | X | X | X | X | X | X | X | -| StartFlow Task (11) | | | X | | | | | | | -| GetInstanceData Task (13) | | | X | | | | | | | -| **Dapr HTTP Task (1)** | | | X | | | | | | | -| **Dapr Service Task (3)** | | | X | | | | | | | -| **Dapr PubSub Task (4)** | | | X | | | | | | | -| **Notification Task (10)** | | | X | | | | | | | -| **Trigger Transition Task (12)** | | | X | | | | | | | -| **SubProcess Task (14)** | | | X | | | | | | | -| **Get Instances Task (15)** | | | X | | | | | | | -| Cancel transition | X | X | | | | | | | | -| **Exit transition (`attributes.exit`)** | X | | | | | | | | | -| **Schedule cancel (manuel)** | X | | | | | | | | | -| **Timer reschedule (self-loop)** | X | | | | | | | | | -| Shared transitions ($self) | | X | | | | | | | | -| **Child-level shared transition** | | X | | | | | | | | -| **SubFlow cancel final states (child/grandchild)** | | X | | | | | | | | -| **Effective state (/functions/state, nested)** | | X | | | | | | | | -| updateData ($self) | | X | | | | X | | | | -| **UpdateData (SubFlow context)** | | X | | | | | | | | -| Master schema | | | | | | X | | | | -| Transition schema | | | | | | X | | | | -| Field roles (master schema) | | | | | | X | | | | -| **queryRoles (workflow/state)** | X | | | | | | | | | -| **Transition roles (state fn. list filter)** | X | | | | | | | | | -| Error boundary (task) | | | | X | | | | | | -| Error boundary (state) | | | | X | | | | | | -| Error boundary (workflow) | | | | X | | | | | | -| Retry policy | | | | X | | | | | | -| **Rollback (action:2)** | | | | X | | | | | | -| **Log action (action:5, state)** | | | | X | | | | | | -| **Notify action (action:4)** | | | | X | | | | | | -| **timeoutPolicy (onTimeout)** | | | | X | | | | | | -| **errorHandlerRule (errorTypes/errorCodes)** | | | | X | | | | | | -| View (JSON/HTML/MD) | | | | | X | | | | | -| Display modlari | | | | | X | | | | | -| Function (single/multi) | | | | | X | | | | | -| Extension (global/req) | | | | | X | | | | | -| **Features referansi** | | | | | X | | | | | -| Workflow timeout | | | | | | | X | | | -| Idempotent start | X | | | | | | X | | | -| Instance filtreleme | | | X | | | | X | | | -| Sayfalama/Siralama | | | X | | | | X | | | -| Hedef workflow yasam dongusu (StartFlow/SubProcess hedefi) | | | X | | | | | | | -| **Condition Task (tip 8) — auto transition rule** | | | X | | | | | | | -| **Timer Task (tip 9) — scheduled transition** | | | X | | | | | | | -| Complementary rules | X | | | | X | | | | | -| **Dapr sidecar + bilesen YAML** | | | X | | | | | | | -| **Flow tipi: Core (C)** | | | | | | | | X | | -| **Flow tipi: SubProcess (P)** | | | | | | | | X | | -| Flow tipi: Flow (F) | X | | X | X | X | X | X | | X | -| Flow tipi: SubFlow (S) | | X | | | | | | | | -| **Version isolation (ayni key, farkli versiyon)** | | | | | | | | | X | -| **Eski instance eski versiyonda kalir** | | | | | | | | | X | +Her grubun hangi vNext ozelliklerini test ettigini gosteren matris. **Gn** sutunu Genel Bakis tablosundaki **Grup n** ile eslesir (1-10; birlestirilmis Dapr senaryosu **G3**). + +| Ozellik | G1 | G2 | G3 | G4 | G5 | G6 | G7 | G8 | G9 | G10 | +|---------|----|----|----|----|----|----|----|----|----|----| +| State tipleri (1/2/3) | X | X | X | X | X | X | X | X | X | X | +| State alt tipleri (1/2/3) | X | X | | X | | | X | | | X | +| **State alt tipleri (4/5/6)** | | | | | | | X | | | | +| SubFlow state (4) | | X | | | | | | | | | +| Wizard state (5) | | | | | X | | | | | | +| Manuel transition (0) | X | X | | | X | X | X | | X | | +| Otomatik transition (1) | X | X | X | X | X | | | X | X | X | +| Zamanlanmis transition (2) | X | | | | | | | | | | +| triggerKind:10 (default) | X | | | | X | | | | | | +| onEntries | X | X | X | X | | | | X | X | X | +| onExits | X | | | | | | | | | | +| startTransition tasks | X | X | X | X | X | X | X | X | X | X | +| IMapping | X | X | X | X | X | X | X | X | X | X | +| IConditionMapping | X | X | X | X | X | | | X | X | X | +| ITimerMapping | X | | | | | | | | | | +| ITransitionMapping | X | | | | | | | | | | +| ISubFlowMapping | | X | | | | | | | | | +| IOutputHandler | | | | | X | | | | | | +| HTTP Task (type:6) | | | X | X | | | | | | | +| Script Task (type:7) | X | X | X | X | X | X | X | X | X | X | +| StartFlow Task (11) | | | X | | | | | | | | +| GetInstanceData Task (13) | | | X | | | | | | | | +| **Dapr HTTP Task (1)** | | | X | | | | | | | | +| **Dapr Service Task (3)** | | | X | | | | | | | | +| **Dapr PubSub Task (4)** | | | X | | | | | | | | +| **Notification Task (10)** | | | X | | | | | | | | +| **Trigger Transition Task (12)** | | | X | | | | | | | | +| **SubProcess Task (14)** | | | X | | | | | | | | +| **Get Instances Task (15)** | | | X | | | | | | | | +| Cancel transition | X | X | | | | | | | | | +| **Exit transition (`attributes.exit`)** | X | | | | | | | | | | +| **Schedule cancel (manuel)** | X | | | | | | | | | | +| **Timer reschedule (self-loop)** | X | | | | | | | | | | +| Shared transitions ($self) | | X | | | | | | | | | +| **Child-level shared transition** | | X | | | | | | | | | +| **SubFlow cancel final states (child/grandchild)** | | X | | | | | | | | | +| **Effective state (/functions/state, nested)** | | X | | | | | | | | | +| updateData ($self) | | X | | | | X | | | | | +| **UpdateData (SubFlow context)** | | X | | | | | | | | | +| Master schema | | | | | | X | | | | | +| Transition schema | | | | | | X | | | | | +| Field roles (master schema) | | | | | | X | | | | | +| **queryRoles (workflow/state)** | X | | | | | | | | | | +| **Transition roles (state fn. list filter)** | X | | | | | | | | | | +| Error boundary (task) | | | | X | | | | | | | +| Error boundary (state) | | | | X | | | | | | | +| Error boundary (workflow) | | | | X | | | | | | | +| Retry policy | | | | X | | | | | | | +| **Rollback (action:2)** | | | | X | | | | | | | +| **Log action (action:5, state)** | | | | X | | | | | | | +| **Notify action (action:4)** | | | | X | | | | | | | +| **timeoutPolicy (onTimeout)** | | | | X | | | | | | | +| **errorHandlerRule (errorTypes/errorCodes)** | | | | X | | | | | | | +| View (JSON/HTML/MD) | | | | | X | | | | | | +| Display modlari | | | | | X | | | | | | +| Function (single/multi) | | | | | X | | | | | | +| Extension (global/req) | | | | | X | | | | | | +| **Features referansi** | | | | | X | | | | | | +| Workflow timeout | | | | | | | X | | | | +| Idempotent start | X | | | | | | X | | | | +| Instance filtreleme | | | X | | | | X | | | | +| Sayfalama/Siralama | | | X | | | | X | | | | +| Hedef workflow yasam dongusu (StartFlow/SubProcess hedefi) | | | X | | | | | | | | +| **Condition Task (tip 8) — auto transition rule** | | | X | | | | | | | | +| **Timer Task (tip 9) — scheduled transition** | | | X | | | | | | | | +| Complementary rules | X | | | | X | | | | | | +| **Dapr sidecar + bilesen YAML** | | | X | | | | | | | | +| **Flow tipi: Core (C)** | | | | | | | | X | | | +| **Flow tipi: SubProcess (P)** | | | | | | | | X | | | +| Flow tipi: Flow (F) | X | | X | X | X | X | X | | X | X | +| Flow tipi: SubFlow (S) | | X | | | | | | | | | +| **Version isolation (ayni key, farkli versiyon)** | | | | | | | | | X | | +| **Eski instance eski versiyonda kalir** | | | | | | | | | X | | +| **subFlow override timeout** | | X | | | | | | | | | +| **subFlow override queryRoles (state)** | | X | | | | | | | | | +| **subFlow override transition roles** | | X | | | | | | | | | +| **ScriptBase dinamik koleksiyon API'leri** (CreateList, ListAdd, ListRemove, ListFilter, ListCount, ListAny, ListFirst, ListLast, ListSelect) | | | | | | | | | | X | +| **ScriptBase dinamik nesne API'leri** (CreateObject, SetProperty, RemoveProperty, HasProperty, ToDictionary) | | | | | | | | | | X | +| **AsList / GetList edge case** (null, non-list) | | | | | | | | | | X | --- diff --git a/vnext-example.sln.DotSettings.user b/vnext-example.sln.DotSettings.user deleted file mode 100644 index 35732dd..0000000 --- a/vnext-example.sln.DotSettings.user +++ /dev/null @@ -1,4 +0,0 @@ - - <SessionState ContinuousTestingMode="0" Name="All tests from Core.IntegrationTests.Tests.FlowTypes" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Namespace>Core.IntegrationTests.Tests.FlowTypes</Namespace> -</SessionState> \ No newline at end of file From f43b1767407e9f86642a7c3962f7a782ac3db6a7 Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Mon, 4 May 2026 17:17:40 +0300 Subject: [PATCH 15/24] collection-object-test as a script base test has added --- .../collection-object-test-workflow.http | 36 ++++ core.link.json | 2 +- .../collection-object-script-task.json | 12 ++ ...llection-object-test-workflow.diagram.json | 3 + .../collection-object-test-workflow.json | 34 ++-- .../src/CreateAndSetMapping.csx | 58 +++--- .../src/GetListAndAsListMapping.csx | 70 +++---- .../src/InitCollectionTestMapping.csx | 17 +- .../src/ListAddRemoveMapping.csx | 82 ++++---- .../src/ListFilterCountAnyMapping.csx | 88 ++++----- .../src/ListFirstLastMapping.csx | 88 ++++----- .../src/ListSelectMapping.csx | 84 ++++---- .../src/RemovePropertyToDictionaryMapping.csx | 93 ++++----- .../integration-test-documentation.md | 21 +- .../CollectionObjectInstanceDataAssertions.cs | 187 ++++++++++++++++++ .../CollectionObjectTestWorkflowTests.cs | 42 ++++ 16 files changed, 580 insertions(+), 337 deletions(-) create mode 100644 api-tests/collection-object-test/collection-object-test-workflow.http create mode 100644 core/Tasks/collection-object-test/collection-object-script-task.json create mode 100644 core/Workflows/collection-object-test/.meta/collection-object-test-workflow.diagram.json rename {doc => docs}/integration-test-documentation.md (98%) create mode 100644 tests/Core/Tests/collection-object-test/CollectionObjectInstanceDataAssertions.cs create mode 100644 tests/Core/Tests/collection-object-test/CollectionObjectTestWorkflowTests.cs diff --git a/api-tests/collection-object-test/collection-object-test-workflow.http b/api-tests/collection-object-test/collection-object-test-workflow.http new file mode 100644 index 0000000..b11a88b --- /dev/null +++ b/api-tests/collection-object-test/collection-object-test-workflow.http @@ -0,0 +1,36 @@ +@baseUrl = http://localhost:4201 +@apiVersion = 1 +@domain = core +@workflow = collection-object-test-workflow + +### +# Grup 10: Dynamic Collection & Object — collection-object-test-workflow +# +# Akis: start-collection-test (onExecutionTasks: InitCollectionTestMapping) +# -> test-create-and-set-state -> ... -> auto zincir (AlwaysTrueRule) +# -> test-completed-state (Final) +# +# Tum ara gecisler otomatik (triggerType: 1); manuel PATCH gerekmez. +# Beklenen son state: test-completed-state; status genelde "C" (Completed). +# attributes icinde mapping sonuc bloklari: testId, createAndSetResult, getListResult, +# filterCountAnyResult, firstLastResult, listSelectResult, listAddRemoveResult, removeToDictResult. +### + +### Adim 1: Workflow baslat +# @name createInstance +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start +Content-Type: application/json + +{ + "key": "collection-object-{{$timestamp}}", + "tags": ["integration-test", "collection-object-test"], + "attributes": {} +} + +@instanceId = {{createInstance.response.body.$.id}} + +### Adim 2: Son durum (beklenen state: test-completed-state) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state + +### Adim 3: Instance verisi (ScriptBase pipeline sonuc bayraklari) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/data diff --git a/core.link.json b/core.link.json index 8652e01..9af3134 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-05-04T12:07:20.021Z" + "importedAt": "2026-05-04T14:00:05.701Z" } \ No newline at end of file diff --git a/core/Tasks/collection-object-test/collection-object-script-task.json b/core/Tasks/collection-object-test/collection-object-script-task.json new file mode 100644 index 0000000..964bf5c --- /dev/null +++ b/core/Tasks/collection-object-test/collection-object-script-task.json @@ -0,0 +1,12 @@ +{ + "key": "collection-object-script-task", + "version": "1.0.0", + "domain": "core", + "flow": "sys-tasks", + "flowVersion": "1.0.0", + "tags": ["integration-test", "collection-object-test", "script", "type-7"], + "attributes": { + "type": "7", + "config": {} + } +} diff --git a/core/Workflows/collection-object-test/.meta/collection-object-test-workflow.diagram.json b/core/Workflows/collection-object-test/.meta/collection-object-test-workflow.diagram.json new file mode 100644 index 0000000..00a4467 --- /dev/null +++ b/core/Workflows/collection-object-test/.meta/collection-object-test-workflow.diagram.json @@ -0,0 +1,3 @@ +{ + "nodePos": {} +} \ No newline at end of file diff --git a/core/Workflows/collection-object-test/collection-object-test-workflow.json b/core/Workflows/collection-object-test/collection-object-test-workflow.json index 34d756f..b3a5263 100644 --- a/core/Workflows/collection-object-test/collection-object-test-workflow.json +++ b/core/Workflows/collection-object-test/collection-object-test-workflow.json @@ -54,14 +54,14 @@ { "order": 1, "task": { - "key": "script-task", + "key": "collection-object-script-task", "domain": "core", "version": "1.0.0", "flow": "sys-tasks" }, "mapping": { "location": "./src/InitCollectionTestMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gSW5pdGlhbCB0cmFuc2l0aW9uIG1hcHBpbmcgLSBJbml0aWFsaXplcyB0aGUgY29sbGVjdGlvbi9vYmplY3QgdGVzdCB3b3JrZmxvdwovLy8gPC9zdW1tYXJ5PgpwdWJsaWMgY2xhc3MgSW5pdENvbGxlY3Rpb25UZXN0TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkNvbGxlY3Rpb25PYmplY3RUZXN0IC0gV29ya2Zsb3cgaW5pdGlhbGl6ZWQiKTsKCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICB7CiAgICAgICAgICAgIEtleSA9ICJpbml0LXN1Y2Nlc3MiLAogICAgICAgICAgICBEYXRhID0gbmV3CiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIHRlc3RJZCA9IEd1aWQuTmV3R3VpZCgpLlRvU3RyaW5nKCksCiAgICAgICAgICAgICAgICBzdGFydGVkQXQgPSBEYXRlVGltZS5VdGNOb3cKICAgICAgICAgICAgfSwKICAgICAgICAgICAgVGFncyA9IG5ld1tdIHsgImNvbGxlY3Rpb24tb2JqZWN0LXRlc3QiLCAiaW5pdGlhbGl6ZWQiIH0KICAgICAgICB9KTsKICAgIH0KfQo=" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIEluaXRDb2xsZWN0aW9uVGVzdE1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJDb2xsZWN0aW9uT2JqZWN0VGVzdCAtIFdvcmtmbG93IGluaXRpYWxpemVkIik7CgogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudGVzdElkID0gR3VpZC5OZXdHdWlkKCkuVG9TdHJpbmcoKTsKICAgICAgICByZXN1bHQuc3RhcnRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgewogICAgICAgICAgICBEYXRhID0gcmVzdWx0CiAgICAgICAgfSk7CiAgICB9Cn0K" } } ] @@ -84,14 +84,14 @@ { "order": 1, "task": { - "key": "script-task", + "key": "collection-object-script-task", "domain": "core", "version": "1.0.0", "flow": "sys-tasks" }, "mapping": { "location": "./src/CreateAndSetMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gVGVzdHM6IENyZWF0ZU9iamVjdCgpLCBDcmVhdGVMaXN0KCksIFNldFByb3BlcnR5KCksIExpc3RBZGQoKQovLy8gQ3JlYXRlcyBhIGxpc3Qgb2YgMyBkeW5hbWljIHBlcnNvbiBvYmplY3RzIGFuZCBzdG9yZXMgdGhlbSBpbiBJbnN0YW5jZS5EYXRhLgovLy8gPC9zdW1tYXJ5PgpwdWJsaWMgY2xhc3MgQ3JlYXRlQW5kU2V0TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdHJ5CiAgICAgICAgewogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiQ3JlYXRlQW5kU2V0IFRlc3QgLSBTdGFydGluZyIpOwoKICAgICAgICAgICAgLy8gVGVzdCBDcmVhdGVPYmplY3QoKSArIFNldFByb3BlcnR5KCkKICAgICAgICAgICAgZHluYW1pYyBpdGVtMSA9IENyZWF0ZU9iamVjdCgpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShpdGVtMSwgImlkIiwgIml0ZW0tMDAxIik7CiAgICAgICAgICAgIFNldFByb3BlcnR5KGl0ZW0xLCAibmFtZSIsICJBbGljZSIpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShpdGVtMSwgImFnZSIsIDMwKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkoaXRlbTEsICJzdGF0dXMiLCAiYWN0aXZlIik7CgogICAgICAgICAgICBkeW5hbWljIGl0ZW0yID0gQ3JlYXRlT2JqZWN0KCk7CiAgICAgICAgICAgIFNldFByb3BlcnR5KGl0ZW0yLCAiaWQiLCAiaXRlbS0wMDIiKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkoaXRlbTIsICJuYW1lIiwgIkJvYiIpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShpdGVtMiwgImFnZSIsIDI1KTsKICAgICAgICAgICAgU2V0UHJvcGVydHkoaXRlbTIsICJzdGF0dXMiLCAiaW5hY3RpdmUiKTsKCiAgICAgICAgICAgIGR5bmFtaWMgaXRlbTMgPSBDcmVhdGVPYmplY3QoKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkoaXRlbTMsICJpZCIsICJpdGVtLTAwMyIpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShpdGVtMywgIm5hbWUiLCAiQ2hhcmxpZSIpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShpdGVtMywgImFnZSIsIDM1KTsKICAgICAgICAgICAgU2V0UHJvcGVydHkoaXRlbTMsICJzdGF0dXMiLCAiYWN0aXZlIik7CgogICAgICAgICAgICAvLyBUZXN0IENyZWF0ZUxpc3QoKSArIExpc3RBZGQoKQogICAgICAgICAgICB2YXIgaXRlbXMgPSBDcmVhdGVMaXN0KCk7CiAgICAgICAgICAgIExpc3RBZGQoaXRlbXMsIGl0ZW0xKTsKICAgICAgICAgICAgTGlzdEFkZChpdGVtcywgaXRlbTIpOwogICAgICAgICAgICBMaXN0QWRkKGl0ZW1zLCBpdGVtMyk7CgogICAgICAgICAgICAvLyBOZXN0ZWQgZHluYW1pYyBvYmplY3QgZm9yIG1ldGFkYXRhCiAgICAgICAgICAgIGR5bmFtaWMgbWV0YWRhdGEgPSBDcmVhdGVPYmplY3QoKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkobWV0YWRhdGEsICJjcmVhdGVkQXQiLCBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIk8iKSk7CiAgICAgICAgICAgIFNldFByb3BlcnR5KG1ldGFkYXRhLCAic291cmNlIiwgImNvbGxlY3Rpb24tb2JqZWN0LXRlc3QiKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkobWV0YWRhdGEsICJpdGVtQ291bnQiLCBpdGVtcy5Db3VudCk7CgogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiQ3JlYXRlQW5kU2V0IFRlc3QgLSBDcmVhdGVkIHswfSBpdGVtcyIsIGFyZ3M6IG5ldyBvYmplY3RbXSB7IGl0ZW1zLkNvdW50IH0pOwoKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gImNyZWF0ZS1hbmQtc2V0LXN1Y2Nlc3MiLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldwogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIGl0ZW1zID0gaXRlbXMsCiAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGEgPSBtZXRhZGF0YSwKICAgICAgICAgICAgICAgICAgICBjcmVhdGVBbmRTZXRSZXN1bHQgPSBuZXcKICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3MgPSB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICBvYmplY3RzQ3JlYXRlZCA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgIGxpc3RJdGVtQ291bnQgPSBpdGVtcy5Db3VudCwKICAgICAgICAgICAgICAgICAgICAgICAgcHJvcGVydGllc1NldCA9IHRydWUKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgVGFncyA9IG5ld1tdIHsgImNvbGxlY3Rpb24tb2JqZWN0LXRlc3QiLCAiY3JlYXRlLWFuZC1zZXQiLCAic3VjY2VzcyIgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCJDcmVhdGVBbmRTZXQgVGVzdCAtIEZhaWxlZDogezB9IiwgYXJnczogbmV3IG9iamVjdFtdIHsgZXguTWVzc2FnZSB9KTsKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gImNyZWF0ZS1hbmQtc2V0LWVycm9yIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcgeyBlcnJvciA9IGV4Lk1lc3NhZ2UgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICB9Cn0K" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIENyZWF0ZUFuZFNldE1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIkNyZWF0ZUFuZFNldCBUZXN0IC0gU3RhcnRpbmciKTsKCiAgICAgICAgICAgIGR5bmFtaWMgaXRlbTEgPSBDcmVhdGVPYmplY3QoKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkoaXRlbTEsICJpZCIsICJpdGVtLTAwMSIpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShpdGVtMSwgIm5hbWUiLCAiQWxpY2UiKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkoaXRlbTEsICJhZ2UiLCAzMCk7CiAgICAgICAgICAgIFNldFByb3BlcnR5KGl0ZW0xLCAic3RhdHVzIiwgImFjdGl2ZSIpOwoKICAgICAgICAgICAgZHluYW1pYyBpdGVtMiA9IENyZWF0ZU9iamVjdCgpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShpdGVtMiwgImlkIiwgIml0ZW0tMDAyIik7CiAgICAgICAgICAgIFNldFByb3BlcnR5KGl0ZW0yLCAibmFtZSIsICJCb2IiKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkoaXRlbTIsICJhZ2UiLCAyNSk7CiAgICAgICAgICAgIFNldFByb3BlcnR5KGl0ZW0yLCAic3RhdHVzIiwgImluYWN0aXZlIik7CgogICAgICAgICAgICBkeW5hbWljIGl0ZW0zID0gQ3JlYXRlT2JqZWN0KCk7CiAgICAgICAgICAgIFNldFByb3BlcnR5KGl0ZW0zLCAiaWQiLCAiaXRlbS0wMDMiKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkoaXRlbTMsICJuYW1lIiwgIkNoYXJsaWUiKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkoaXRlbTMsICJhZ2UiLCAzNSk7CiAgICAgICAgICAgIFNldFByb3BlcnR5KGl0ZW0zLCAic3RhdHVzIiwgImFjdGl2ZSIpOwoKICAgICAgICAgICAgdmFyIGl0ZW1zID0gQ3JlYXRlTGlzdCgpOwogICAgICAgICAgICBMaXN0QWRkKGl0ZW1zLCBpdGVtMSk7CiAgICAgICAgICAgIExpc3RBZGQoaXRlbXMsIGl0ZW0yKTsKICAgICAgICAgICAgTGlzdEFkZChpdGVtcywgaXRlbTMpOwoKICAgICAgICAgICAgZHluYW1pYyBtZXRhZGF0YSA9IENyZWF0ZU9iamVjdCgpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShtZXRhZGF0YSwgImNyZWF0ZWRBdCIsIERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkobWV0YWRhdGEsICJzb3VyY2UiLCAiY29sbGVjdGlvbi1vYmplY3QtdGVzdCIpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShtZXRhZGF0YSwgIml0ZW1Db3VudCIsIGl0ZW1zLkNvdW50KTsKCiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCQiQ3JlYXRlQW5kU2V0IFRlc3QgLSBDcmVhdGVkIHtpdGVtcy5Db3VudH0gaXRlbXMiKTsKCiAgICAgICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRBdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnN0YXJ0ZWRBdCA9IGRhdGEuc3RhcnRlZEF0OwoKICAgICAgICAgICAgcmVzdWx0Lml0ZW1zID0gaXRlbXM7CiAgICAgICAgICAgIHJlc3VsdC5tZXRhZGF0YSA9IG1ldGFkYXRhOwoKICAgICAgICAgICAgcmVzdWx0LmNyZWF0ZUFuZFNldFJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgICAgIHJlc3VsdC5jcmVhdGVBbmRTZXRSZXN1bHQuc3VjY2VzcyA9IHRydWU7CiAgICAgICAgICAgIHJlc3VsdC5jcmVhdGVBbmRTZXRSZXN1bHQub2JqZWN0c0NyZWF0ZWQgPSAzOwogICAgICAgICAgICByZXN1bHQuY3JlYXRlQW5kU2V0UmVzdWx0Lmxpc3RJdGVtQ291bnQgPSBpdGVtcy5Db3VudDsKICAgICAgICAgICAgcmVzdWx0LmNyZWF0ZUFuZFNldFJlc3VsdC5wcm9wZXJ0aWVzU2V0ID0gdHJ1ZTsKCiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCQiQ3JlYXRlQW5kU2V0IFRlc3QgLSBGYWlsZWQ6IHtleC5NZXNzYWdlfSIpOwogICAgICAgICAgICBkeW5hbWljIGVyclJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgICAgIGVyclJlc3VsdC5lcnJvciA9IGV4Lk1lc3NhZ2U7CiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IGVyclJlc3VsdCB9KTsKICAgICAgICB9CiAgICB9Cn0K" } } ], @@ -136,14 +136,14 @@ { "order": 1, "task": { - "key": "script-task", + "key": "collection-object-script-task", "domain": "core", "version": "1.0.0", "flow": "sys-tasks" }, "mapping": { "location": "./src/GetListAndAsListMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gVGVzdHM6IEdldExpc3QoKSwgQXNMaXN0KCkKLy8vIFJldHJpZXZlcyB0aGUgaXRlbXMgbGlzdCBmcm9tIEluc3RhbmNlLkRhdGEgYW5kIHZlcmlmaWVzIEFzTGlzdCgpIGVkZ2UgY2FzZXMuCi8vLyA8L3N1bW1hcnk+CnB1YmxpYyBjbGFzcyBHZXRMaXN0QW5kQXNMaXN0TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdHJ5CiAgICAgICAgewogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiR2V0TGlzdCBhbmQgQXNMaXN0IFRlc3QgLSBTdGFydGluZyIpOwoKICAgICAgICAgICAgdmFyIGluc3RhbmNlRGF0YSA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGE7CgogICAgICAgICAgICAvLyBUZXN0IEdldExpc3QoKSAtIHJldHJpZXZlIGl0ZW1zIGZyb20gaW5zdGFuY2UgZGF0YSBieSBwcm9wZXJ0eSBuYW1lCiAgICAgICAgICAgIHZhciBpdGVtcyA9IEdldExpc3QoaW5zdGFuY2VEYXRhLCAiaXRlbXMiKTsKCiAgICAgICAgICAgIC8vIFRlc3QgQXNMaXN0KCkgd2l0aCBhIHZhbGlkIGxpc3QgLSBzaG91bGQgcmV0dXJuIHNhbWUgaXRlbXMKICAgICAgICAgICAgdmFyIGFzTGlzdEZyb21WYWxpZCA9IEFzTGlzdChpdGVtcyk7CgogICAgICAgICAgICAvLyBUZXN0IEFzTGlzdCgpIHdpdGggbnVsbCAtIHNob3VsZCByZXR1cm4gZW1wdHkgbGlzdCAoZ3JhY2VmdWwgZGVncmFkYXRpb24pCiAgICAgICAgICAgIHZhciBhc0xpc3RGcm9tTnVsbCA9IEFzTGlzdChudWxsKTsKCiAgICAgICAgICAgIC8vIFRlc3QgQXNMaXN0KCkgd2l0aCBhIG5vbi1saXN0IHZhbHVlIC0gc2hvdWxkIHJldHVybiBlbXB0eSBsaXN0CiAgICAgICAgICAgIHZhciBhc0xpc3RGcm9tU3RyaW5nID0gQXNMaXN0KCJub3QgYSBsaXN0Iik7CgogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiR2V0TGlzdCBUZXN0IC0gUmV0cmlldmVkIHswfSBpdGVtcyIsIGFyZ3M6IG5ldyBvYmplY3RbXSB7IGl0ZW1zLkNvdW50IH0pOwoKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gImdldC1saXN0LXN1Y2Nlc3MiLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldwogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIGdldExpc3RSZXN1bHQgPSBuZXcKICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3MgPSB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICBpdGVtQ291bnQgPSBpdGVtcy5Db3VudCwKICAgICAgICAgICAgICAgICAgICAgICAgZ2V0TGlzdFdvcmtlZCA9IGl0ZW1zLkNvdW50ID09IDMsCiAgICAgICAgICAgICAgICAgICAgICAgIGFzTGlzdEZyb21WYWxpZENvdW50ID0gYXNMaXN0RnJvbVZhbGlkLkNvdW50LAogICAgICAgICAgICAgICAgICAgICAgICBhc0xpc3ROdWxsUmV0dXJuc0VtcHR5ID0gYXNMaXN0RnJvbU51bGwuQ291bnQgPT0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgYXNMaXN0SW52YWxpZFJldHVybnNFbXB0eSA9IGFzTGlzdEZyb21TdHJpbmcuQ291bnQgPT0gMAogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICBUYWdzID0gbmV3W10geyAiY29sbGVjdGlvbi1vYmplY3QtdGVzdCIsICJnZXQtbGlzdCIsICJhcy1saXN0IiwgInN1Y2Nlc3MiIH0KICAgICAgICAgICAgfSk7CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICBMb2dFcnJvcigiR2V0TGlzdCBUZXN0IC0gRmFpbGVkOiB7MH0iLCBhcmdzOiBuZXcgb2JqZWN0W10geyBleC5NZXNzYWdlIH0pOwogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAiZ2V0LWxpc3QtZXJyb3IiLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldyB7IGVycm9yID0gZXguTWVzc2FnZSB9CiAgICAgICAgICAgIH0pOwogICAgICAgIH0KICAgIH0KfQo=" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIEdldExpc3RBbmRBc0xpc3RNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJHZXRMaXN0IGFuZCBBc0xpc3QgVGVzdCAtIFN0YXJ0aW5nIik7CgogICAgICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKCiAgICAgICAgICAgIHZhciBpdGVtcyA9IEdldExpc3QoZGF0YSwgIml0ZW1zIik7CiAgICAgICAgICAgIHZhciBhc0xpc3RGcm9tVmFsaWQgPSBBc0xpc3QoaXRlbXMpOwogICAgICAgICAgICB2YXIgYXNMaXN0RnJvbU51bGwgPSBBc0xpc3QobnVsbCk7CiAgICAgICAgICAgIHZhciBhc0xpc3RGcm9tU3RyaW5nID0gQXNMaXN0KCJub3QgYSBsaXN0Iik7CgogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkdldExpc3QgVGVzdCAtIFJldHJpZXZlZCB7aXRlbXMuQ291bnR9IGl0ZW1zIik7CgogICAgICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRBdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnN0YXJ0ZWRBdCA9IGRhdGEuc3RhcnRlZEF0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgIml0ZW1zIikpCiAgICAgICAgICAgICAgICByZXN1bHQuaXRlbXMgPSBkYXRhLml0ZW1zOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgIm1ldGFkYXRhIikpCiAgICAgICAgICAgICAgICByZXN1bHQubWV0YWRhdGEgPSBkYXRhLm1ldGFkYXRhOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyZWF0ZUFuZFNldFJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmNyZWF0ZUFuZFNldFJlc3VsdCA9IGRhdGEuY3JlYXRlQW5kU2V0UmVzdWx0OwoKICAgICAgICAgICAgcmVzdWx0LmdldExpc3RSZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgICAgICByZXN1bHQuZ2V0TGlzdFJlc3VsdC5zdWNjZXNzID0gdHJ1ZTsKICAgICAgICAgICAgcmVzdWx0LmdldExpc3RSZXN1bHQuaXRlbUNvdW50ID0gaXRlbXMuQ291bnQ7CiAgICAgICAgICAgIHJlc3VsdC5nZXRMaXN0UmVzdWx0LmdldExpc3RXb3JrZWQgPSBpdGVtcy5Db3VudCA9PSAzOwogICAgICAgICAgICByZXN1bHQuZ2V0TGlzdFJlc3VsdC5hc0xpc3RGcm9tVmFsaWRDb3VudCA9IGFzTGlzdEZyb21WYWxpZC5Db3VudDsKICAgICAgICAgICAgcmVzdWx0LmdldExpc3RSZXN1bHQuYXNMaXN0TnVsbFJldHVybnNFbXB0eSA9IGFzTGlzdEZyb21OdWxsLkNvdW50ID09IDA7CiAgICAgICAgICAgIHJlc3VsdC5nZXRMaXN0UmVzdWx0LmFzTGlzdEludmFsaWRSZXR1cm5zRW1wdHkgPSBhc0xpc3RGcm9tU3RyaW5nLkNvdW50ID09IDA7CgogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICBMb2dFcnJvcigkIkdldExpc3QgVGVzdCAtIEZhaWxlZDoge2V4Lk1lc3NhZ2V9Iik7CiAgICAgICAgICAgIGR5bmFtaWMgZXJyUmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICAgICAgZXJyUmVzdWx0LmVycm9yID0gZXguTWVzc2FnZTsKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gZXJyUmVzdWx0IH0pOwogICAgICAgIH0KICAgIH0KfQo=" } } ], @@ -188,14 +188,14 @@ { "order": 1, "task": { - "key": "script-task", + "key": "collection-object-script-task", "domain": "core", "version": "1.0.0", "flow": "sys-tasks" }, "mapping": { "location": "./src/ListFilterCountAnyMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gVGVzdHM6IExpc3RGaWx0ZXIoKSwgTGlzdENvdW50KCksIExpc3RBbnkoKQovLy8gVXNlcyB0aGUgaXRlbXMgbGlzdCAoQWxpY2U9YWN0aXZlLCBCb2I9aW5hY3RpdmUsIENoYXJsaWU9YWN0aXZlKSBmcm9tIEluc3RhbmNlLkRhdGEuCi8vLyA8L3N1bW1hcnk+CnB1YmxpYyBjbGFzcyBMaXN0RmlsdGVyQ291bnRBbnlNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJMaXN0RmlsdGVyLCBMaXN0Q291bnQsIExpc3RBbnkgVGVzdCAtIFN0YXJ0aW5nIik7CgogICAgICAgICAgICBvYmplY3QgaW5zdGFuY2VEYXRhID0gY29udGV4dC5JbnN0YW5jZT8uRGF0YTsKICAgICAgICAgICAgdmFyIGl0ZW1zID0gR2V0TGlzdChpbnN0YW5jZURhdGEsICJpdGVtcyIpOwoKICAgICAgICAgICAgLy8gVGVzdCBMaXN0RmlsdGVyKCkgLSBmaWx0ZXIgYWN0aXZlIGl0ZW1zIChBbGljZSArIENoYXJsaWUpCiAgICAgICAgICAgIHZhciBhY3RpdmVJdGVtcyA9IExpc3RGaWx0ZXIoaXRlbXMsIHggPT4geC5zdGF0dXMgPT0gImFjdGl2ZSIpOwoKICAgICAgICAgICAgLy8gVGVzdCBMaXN0Q291bnQoKSAtIG5vIHByZWRpY2F0ZSAodG90YWwpCiAgICAgICAgICAgIHZhciB0b3RhbENvdW50ID0gTGlzdENvdW50KGl0ZW1zKTsKCiAgICAgICAgICAgIC8vIFRlc3QgTGlzdENvdW50KCkgd2l0aCBwcmVkaWNhdGUKICAgICAgICAgICAgdmFyIGFjdGl2ZUNvdW50ID0gTGlzdENvdW50KGl0ZW1zLCB4ID0+IHguc3RhdHVzID09ICJhY3RpdmUiKTsKICAgICAgICAgICAgdmFyIGluYWN0aXZlQ291bnQgPSBMaXN0Q291bnQoaXRlbXMsIHggPT4geC5zdGF0dXMgPT0gImluYWN0aXZlIik7CgogICAgICAgICAgICAvLyBUZXN0IExpc3RBbnkoKSAtIG5vIHByZWRpY2F0ZSAoaGFzIGFueSBlbGVtZW50KQogICAgICAgICAgICB2YXIgaGFzSXRlbXMgPSBMaXN0QW55KGl0ZW1zKTsKCiAgICAgICAgICAgIC8vIFRlc3QgTGlzdEFueSgpIHdpdGggcHJlZGljYXRlCiAgICAgICAgICAgIHZhciBoYXNBY3RpdmUgPSBMaXN0QW55KGl0ZW1zLCB4ID0+IHguc3RhdHVzID09ICJhY3RpdmUiKTsKICAgICAgICAgICAgdmFyIGhhc0FkbWluUm9sZSA9IExpc3RBbnkoaXRlbXMsIHggPT4geC5zdGF0dXMgPT0gImFkbWluIik7CgogICAgICAgICAgICAvLyBFbXB0eSBsaXN0IGNoZWNrcwogICAgICAgICAgICB2YXIgZW1wdHlMaXN0ID0gQ3JlYXRlTGlzdCgpOwogICAgICAgICAgICB2YXIgZW1wdHlIYXNJdGVtcyA9IExpc3RBbnkoZW1wdHlMaXN0KTsKICAgICAgICAgICAgdmFyIGVtcHR5Q291bnQgPSBMaXN0Q291bnQoZW1wdHlMaXN0KTsKCiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJMaXN0RmlsdGVyOiBhY3RpdmU9ezB9LCBpbmFjdGl2ZT17MX0sIHRvdGFsPXsyfSIsCiAgICAgICAgICAgICAgICBhcmdzOiBuZXcgb2JqZWN0W10geyBhY3RpdmVDb3VudCwgaW5hY3RpdmVDb3VudCwgdG90YWxDb3VudCB9KTsKCiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9ICJmaWx0ZXItY291bnQtYW55LXN1Y2Nlc3MiLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldwogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIGZpbHRlckNvdW50QW55UmVzdWx0ID0gbmV3CiAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICBzdWNjZXNzID0gdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgdG90YWxDb3VudCA9IHRvdGFsQ291bnQsCiAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2ZUNvdW50ID0gYWN0aXZlQ291bnQsCiAgICAgICAgICAgICAgICAgICAgICAgIGluYWN0aXZlQ291bnQgPSBpbmFjdGl2ZUNvdW50LAogICAgICAgICAgICAgICAgICAgICAgICBhY3RpdmVJdGVtc0ZpbHRlcmVkID0gYWN0aXZlSXRlbXMuQ291bnQsCiAgICAgICAgICAgICAgICAgICAgICAgIGhhc0l0ZW1zID0gaGFzSXRlbXMsCiAgICAgICAgICAgICAgICAgICAgICAgIGhhc0FjdGl2ZSA9IGhhc0FjdGl2ZSwKICAgICAgICAgICAgICAgICAgICAgICAgaGFzQWRtaW5Sb2xlID0gaGFzQWRtaW5Sb2xlLAogICAgICAgICAgICAgICAgICAgICAgICBlbXB0eUxpc3RIYXNJdGVtcyA9IGVtcHR5SGFzSXRlbXMsCiAgICAgICAgICAgICAgICAgICAgICAgIGVtcHR5TGlzdENvdW50ID0gZW1wdHlDb3VudCwKICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyV29ya2VkID0gYWN0aXZlSXRlbXMuQ291bnQgPT0gMiwKICAgICAgICAgICAgICAgICAgICAgICAgY291bnRXb3JrZWQgPSB0b3RhbENvdW50ID09IDMgJiYgYWN0aXZlQ291bnQgPT0gMiAmJiBpbmFjdGl2ZUNvdW50ID09IDEsCiAgICAgICAgICAgICAgICAgICAgICAgIGFueVdvcmtlZCA9IGhhc0l0ZW1zICYmIGhhc0FjdGl2ZSAmJiAhaGFzQWRtaW5Sb2xlICYmICFlbXB0eUhhc0l0ZW1zCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgIFRhZ3MgPSBuZXdbXSB7ICJjb2xsZWN0aW9uLW9iamVjdC10ZXN0IiwgImxpc3QtZmlsdGVyIiwgImxpc3QtY291bnQiLCAibGlzdC1hbnkiLCAic3VjY2VzcyIgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCJMaXN0RmlsdGVyL0NvdW50L0FueSBUZXN0IC0gRmFpbGVkOiB7MH0iLCBhcmdzOiBuZXcgb2JqZWN0W10geyBleC5NZXNzYWdlIH0pOwogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAiZmlsdGVyLWNvdW50LWFueS1lcnJvciIsCiAgICAgICAgICAgICAgICBEYXRhID0gbmV3IHsgZXJyb3IgPSBleC5NZXNzYWdlIH0KICAgICAgICAgICAgfSk7CiAgICAgICAgfQogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIExpc3RGaWx0ZXJDb3VudEFueU1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIkxpc3RGaWx0ZXIsIExpc3RDb3VudCwgTGlzdEFueSBUZXN0IC0gU3RhcnRpbmciKTsKCiAgICAgICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgICAgICB2YXIgaXRlbXMgPSBHZXRMaXN0KGRhdGEsICJpdGVtcyIpOwoKICAgICAgICAgICAgdmFyIGFjdGl2ZUl0ZW1zID0gTGlzdEZpbHRlcihpdGVtcywgeCA9PiB4LnN0YXR1cyA9PSAiYWN0aXZlIik7CiAgICAgICAgICAgIHZhciB0b3RhbENvdW50ID0gTGlzdENvdW50KGl0ZW1zKTsKICAgICAgICAgICAgdmFyIGFjdGl2ZUNvdW50ID0gTGlzdENvdW50KGl0ZW1zLCB4ID0+IHguc3RhdHVzID09ICJhY3RpdmUiKTsKICAgICAgICAgICAgdmFyIGluYWN0aXZlQ291bnQgPSBMaXN0Q291bnQoaXRlbXMsIHggPT4geC5zdGF0dXMgPT0gImluYWN0aXZlIik7CiAgICAgICAgICAgIHZhciBoYXNJdGVtcyA9IExpc3RBbnkoaXRlbXMpOwogICAgICAgICAgICB2YXIgaGFzQWN0aXZlID0gTGlzdEFueShpdGVtcywgeCA9PiB4LnN0YXR1cyA9PSAiYWN0aXZlIik7CiAgICAgICAgICAgIHZhciBoYXNBZG1pblJvbGUgPSBMaXN0QW55KGl0ZW1zLCB4ID0+IHguc3RhdHVzID09ICJhZG1pbiIpOwoKICAgICAgICAgICAgdmFyIGVtcHR5TGlzdCA9IENyZWF0ZUxpc3QoKTsKICAgICAgICAgICAgdmFyIGVtcHR5SGFzSXRlbXMgPSBMaXN0QW55KGVtcHR5TGlzdCk7CiAgICAgICAgICAgIHZhciBlbXB0eUNvdW50ID0gTGlzdENvdW50KGVtcHR5TGlzdCk7CgogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkxpc3RGaWx0ZXI6IGFjdGl2ZT17YWN0aXZlQ291bnR9LCBpbmFjdGl2ZT17aW5hY3RpdmVDb3VudH0sIHRvdGFsPXt0b3RhbENvdW50fSIpOwoKICAgICAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkQXQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpdGVtcyIpKQogICAgICAgICAgICAgICAgcmVzdWx0Lml0ZW1zID0gZGF0YS5pdGVtczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJtZXRhZGF0YSIpKQogICAgICAgICAgICAgICAgcmVzdWx0Lm1ldGFkYXRhID0gZGF0YS5tZXRhZGF0YTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcmVhdGVBbmRTZXRSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5jcmVhdGVBbmRTZXRSZXN1bHQgPSBkYXRhLmNyZWF0ZUFuZFNldFJlc3VsdDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJnZXRMaXN0UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuZ2V0TGlzdFJlc3VsdCA9IGRhdGEuZ2V0TGlzdFJlc3VsdDsKCiAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdC5zdWNjZXNzID0gdHJ1ZTsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0LnRvdGFsQ291bnQgPSB0b3RhbENvdW50OwogICAgICAgICAgICByZXN1bHQuZmlsdGVyQ291bnRBbnlSZXN1bHQuYWN0aXZlQ291bnQgPSBhY3RpdmVDb3VudDsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0LmluYWN0aXZlQ291bnQgPSBpbmFjdGl2ZUNvdW50OwogICAgICAgICAgICByZXN1bHQuZmlsdGVyQ291bnRBbnlSZXN1bHQuYWN0aXZlSXRlbXNGaWx0ZXJlZCA9IGFjdGl2ZUl0ZW1zLkNvdW50OwogICAgICAgICAgICByZXN1bHQuZmlsdGVyQ291bnRBbnlSZXN1bHQuaGFzSXRlbXMgPSBoYXNJdGVtczsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0Lmhhc0FjdGl2ZSA9IGhhc0FjdGl2ZTsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0Lmhhc0FkbWluUm9sZSA9IGhhc0FkbWluUm9sZTsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0LmVtcHR5TGlzdEhhc0l0ZW1zID0gZW1wdHlIYXNJdGVtczsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0LmVtcHR5TGlzdENvdW50ID0gZW1wdHlDb3VudDsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0LmZpbHRlcldvcmtlZCA9IGFjdGl2ZUl0ZW1zLkNvdW50ID09IDI7CiAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdC5jb3VudFdvcmtlZCA9IHRvdGFsQ291bnQgPT0gMyAmJiBhY3RpdmVDb3VudCA9PSAyICYmIGluYWN0aXZlQ291bnQgPT0gMTsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0LmFueVdvcmtlZCA9IGhhc0l0ZW1zICYmIGhhc0FjdGl2ZSAmJiAhaGFzQWRtaW5Sb2xlICYmICFlbXB0eUhhc0l0ZW1zOwoKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgICAgIH0KICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGV4KQogICAgICAgIHsKICAgICAgICAgICAgTG9nRXJyb3IoJCJMaXN0RmlsdGVyL0NvdW50L0FueSBUZXN0IC0gRmFpbGVkOiB7ZXguTWVzc2FnZX0iKTsKICAgICAgICAgICAgZHluYW1pYyBlcnJSZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgICAgICBlcnJSZXN1bHQuZXJyb3IgPSBleC5NZXNzYWdlOwogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSBlcnJSZXN1bHQgfSk7CiAgICAgICAgfQogICAgfQp9Cg==" } } ], @@ -240,14 +240,14 @@ { "order": 1, "task": { - "key": "script-task", + "key": "collection-object-script-task", "domain": "core", "version": "1.0.0", "flow": "sys-tasks" }, "mapping": { "location": "./src/ListFirstLastMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gVGVzdHM6IExpc3RGaXJzdCgpLCBMaXN0TGFzdCgpCi8vLyBJdGVtcyBvcmRlcjogQWxpY2UoYWN0aXZlKSwgQm9iKGluYWN0aXZlKSwgQ2hhcmxpZShhY3RpdmUpCi8vLyA8L3N1bW1hcnk+CnB1YmxpYyBjbGFzcyBMaXN0Rmlyc3RMYXN0TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdHJ5CiAgICAgICAgewogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiTGlzdEZpcnN0IGFuZCBMaXN0TGFzdCBUZXN0IC0gU3RhcnRpbmciKTsKCiAgICAgICAgICAgIG9iamVjdCBpbnN0YW5jZURhdGEgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhOwogICAgICAgICAgICB2YXIgaXRlbXMgPSBHZXRMaXN0KGluc3RhbmNlRGF0YSwgIml0ZW1zIik7CgogICAgICAgICAgICAvLyBUZXN0IExpc3RGaXJzdCgpIHdpdGhvdXQgcHJlZGljYXRlIOKGkiBBbGljZQogICAgICAgICAgICB2YXIgZmlyc3QgPSBMaXN0Rmlyc3QoaXRlbXMpOwoKICAgICAgICAgICAgLy8gVGVzdCBMaXN0Rmlyc3QoKSB3aXRoIHByZWRpY2F0ZSDihpIgZmlyc3QgYWN0aXZlID0gQWxpY2UsIGZpcnN0IGluYWN0aXZlID0gQm9iCiAgICAgICAgICAgIHZhciBmaXJzdEFjdGl2ZSA9IExpc3RGaXJzdChpdGVtcywgeCA9PiB4LnN0YXR1cyA9PSAiYWN0aXZlIik7CiAgICAgICAgICAgIHZhciBmaXJzdEluYWN0aXZlID0gTGlzdEZpcnN0KGl0ZW1zLCB4ID0+IHguc3RhdHVzID09ICJpbmFjdGl2ZSIpOwoKICAgICAgICAgICAgLy8gVGVzdCBMaXN0Rmlyc3QoKSB3aXRoIG5vIG1hdGNoIOKGkiBudWxsCiAgICAgICAgICAgIHZhciBmaXJzdE5vdEZvdW5kID0gTGlzdEZpcnN0KGl0ZW1zLCB4ID0+IHguc3RhdHVzID09ICJhZG1pbiIpOwoKICAgICAgICAgICAgLy8gVGVzdCBMaXN0TGFzdCgpIHdpdGhvdXQgcHJlZGljYXRlIOKGkiBDaGFybGllCiAgICAgICAgICAgIHZhciBsYXN0ID0gTGlzdExhc3QoaXRlbXMpOwoKICAgICAgICAgICAgLy8gVGVzdCBMaXN0TGFzdCgpIHdpdGggcHJlZGljYXRlIOKGkiBsYXN0IGFjdGl2ZSA9IENoYXJsaWUKICAgICAgICAgICAgdmFyIGxhc3RBY3RpdmUgPSBMaXN0TGFzdChpdGVtcywgeCA9PiB4LnN0YXR1cyA9PSAiYWN0aXZlIik7CgogICAgICAgICAgICAvLyBUZXN0IG9uIGVtcHR5IGxpc3Qg4oaSIG51bGwKICAgICAgICAgICAgdmFyIGVtcHR5Rmlyc3QgPSBMaXN0Rmlyc3QoQ3JlYXRlTGlzdCgpKTsKICAgICAgICAgICAgdmFyIGVtcHR5TGFzdCA9IExpc3RMYXN0KENyZWF0ZUxpc3QoKSk7CgogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiRmlyc3Q6IHswfSwgTGFzdDogezF9IiwgYXJnczogbmV3IG9iamVjdFtdIHsgZmlyc3Q/Lm5hbWUsIGxhc3Q/Lm5hbWUgfSk7CgogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAiZmlyc3QtbGFzdC1zdWNjZXNzIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBmaXJzdExhc3RSZXN1bHQgPSBuZXcKICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3MgPSB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICBmaXJzdE5hbWUgPSAoc3RyaW5nKShmaXJzdD8ubmFtZSA/PyAibnVsbCIpLAogICAgICAgICAgICAgICAgICAgICAgICBsYXN0TmFtZSA9IChzdHJpbmcpKGxhc3Q/Lm5hbWUgPz8gIm51bGwiKSwKICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3RBY3RpdmVOYW1lID0gKHN0cmluZykoZmlyc3RBY3RpdmU/Lm5hbWUgPz8gIm51bGwiKSwKICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3RJbmFjdGl2ZU5hbWUgPSAoc3RyaW5nKShmaXJzdEluYWN0aXZlPy5uYW1lID8/ICJudWxsIiksCiAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RBY3RpdmVOYW1lID0gKHN0cmluZykobGFzdEFjdGl2ZT8ubmFtZSA/PyAibnVsbCIpLAogICAgICAgICAgICAgICAgICAgICAgICBmaXJzdE5vdEZvdW5kSXNOdWxsID0gZmlyc3ROb3RGb3VuZCA9PSBudWxsLAogICAgICAgICAgICAgICAgICAgICAgICBlbXB0eUZpcnN0SXNOdWxsID0gZW1wdHlGaXJzdCA9PSBudWxsLAogICAgICAgICAgICAgICAgICAgICAgICBlbXB0eUxhc3RJc051bGwgPSBlbXB0eUxhc3QgPT0gbnVsbCwKICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3RXb3JrZWQgPSBmaXJzdD8uaWQgPT0gIml0ZW0tMDAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgbGFzdFdvcmtlZCA9IGxhc3Q/LmlkID09ICJpdGVtLTAwMyIsCiAgICAgICAgICAgICAgICAgICAgICAgIGZpcnN0V2l0aFByZWRpY2F0ZVdvcmtlZCA9IGZpcnN0QWN0aXZlPy5pZCA9PSAiaXRlbS0wMDEiLAogICAgICAgICAgICAgICAgICAgICAgICBsYXN0V2l0aFByZWRpY2F0ZVdvcmtlZCA9IGxhc3RBY3RpdmU/LmlkID09ICJpdGVtLTAwMyIKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgVGFncyA9IG5ld1tdIHsgImNvbGxlY3Rpb24tb2JqZWN0LXRlc3QiLCAibGlzdC1maXJzdCIsICJsaXN0LWxhc3QiLCAic3VjY2VzcyIgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCJMaXN0Rmlyc3QvTGFzdCBUZXN0IC0gRmFpbGVkOiB7MH0iLCBhcmdzOiBuZXcgb2JqZWN0W10geyBleC5NZXNzYWdlIH0pOwogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAiZmlyc3QtbGFzdC1lcnJvciIsCiAgICAgICAgICAgICAgICBEYXRhID0gbmV3IHsgZXJyb3IgPSBleC5NZXNzYWdlIH0KICAgICAgICAgICAgfSk7CiAgICAgICAgfQogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIExpc3RGaXJzdExhc3RNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJMaXN0Rmlyc3QgYW5kIExpc3RMYXN0IFRlc3QgLSBTdGFydGluZyIpOwoKICAgICAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgICAgIHZhciBpdGVtcyA9IEdldExpc3QoZGF0YSwgIml0ZW1zIik7CgogICAgICAgICAgICB2YXIgZmlyc3QgPSBMaXN0Rmlyc3QoaXRlbXMpOwogICAgICAgICAgICB2YXIgZmlyc3RBY3RpdmUgPSBMaXN0Rmlyc3QoaXRlbXMsIHggPT4geC5zdGF0dXMgPT0gImFjdGl2ZSIpOwogICAgICAgICAgICB2YXIgZmlyc3RJbmFjdGl2ZSA9IExpc3RGaXJzdChpdGVtcywgeCA9PiB4LnN0YXR1cyA9PSAiaW5hY3RpdmUiKTsKICAgICAgICAgICAgdmFyIGZpcnN0Tm90Rm91bmQgPSBMaXN0Rmlyc3QoaXRlbXMsIHggPT4geC5zdGF0dXMgPT0gImFkbWluIik7CgogICAgICAgICAgICB2YXIgbGFzdCA9IExpc3RMYXN0KGl0ZW1zKTsKICAgICAgICAgICAgdmFyIGxhc3RBY3RpdmUgPSBMaXN0TGFzdChpdGVtcywgeCA9PiB4LnN0YXR1cyA9PSAiYWN0aXZlIik7CgogICAgICAgICAgICB2YXIgZW1wdHlGaXJzdCA9IExpc3RGaXJzdChDcmVhdGVMaXN0KCkpOwogICAgICAgICAgICB2YXIgZW1wdHlMYXN0ID0gTGlzdExhc3QoQ3JlYXRlTGlzdCgpKTsKCiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCQiRmlyc3Q6IHtmaXJzdD8ubmFtZX0sIExhc3Q6IHtsYXN0Py5uYW1lfSIpOwoKICAgICAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkQXQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpdGVtcyIpKQogICAgICAgICAgICAgICAgcmVzdWx0Lml0ZW1zID0gZGF0YS5pdGVtczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJtZXRhZGF0YSIpKQogICAgICAgICAgICAgICAgcmVzdWx0Lm1ldGFkYXRhID0gZGF0YS5tZXRhZGF0YTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcmVhdGVBbmRTZXRSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5jcmVhdGVBbmRTZXRSZXN1bHQgPSBkYXRhLmNyZWF0ZUFuZFNldFJlc3VsdDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJnZXRMaXN0UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuZ2V0TGlzdFJlc3VsdCA9IGRhdGEuZ2V0TGlzdFJlc3VsdDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJmaWx0ZXJDb3VudEFueVJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0ID0gZGF0YS5maWx0ZXJDb3VudEFueVJlc3VsdDsKCiAgICAgICAgICAgIHJlc3VsdC5maXJzdExhc3RSZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgICAgICByZXN1bHQuZmlyc3RMYXN0UmVzdWx0LnN1Y2Nlc3MgPSB0cnVlOwogICAgICAgICAgICByZXN1bHQuZmlyc3RMYXN0UmVzdWx0LmZpcnN0TmFtZSA9IChzdHJpbmcpKGZpcnN0Py5uYW1lID8/ICJudWxsIik7CiAgICAgICAgICAgIHJlc3VsdC5maXJzdExhc3RSZXN1bHQubGFzdE5hbWUgPSAoc3RyaW5nKShsYXN0Py5uYW1lID8/ICJudWxsIik7CiAgICAgICAgICAgIHJlc3VsdC5maXJzdExhc3RSZXN1bHQuZmlyc3RBY3RpdmVOYW1lID0gKHN0cmluZykoZmlyc3RBY3RpdmU/Lm5hbWUgPz8gIm51bGwiKTsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5maXJzdEluYWN0aXZlTmFtZSA9IChzdHJpbmcpKGZpcnN0SW5hY3RpdmU/Lm5hbWUgPz8gIm51bGwiKTsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5sYXN0QWN0aXZlTmFtZSA9IChzdHJpbmcpKGxhc3RBY3RpdmU/Lm5hbWUgPz8gIm51bGwiKTsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5maXJzdE5vdEZvdW5kSXNOdWxsID0gZmlyc3ROb3RGb3VuZCA9PSBudWxsOwogICAgICAgICAgICByZXN1bHQuZmlyc3RMYXN0UmVzdWx0LmVtcHR5Rmlyc3RJc051bGwgPSBlbXB0eUZpcnN0ID09IG51bGw7CiAgICAgICAgICAgIHJlc3VsdC5maXJzdExhc3RSZXN1bHQuZW1wdHlMYXN0SXNOdWxsID0gZW1wdHlMYXN0ID09IG51bGw7CiAgICAgICAgICAgIHJlc3VsdC5maXJzdExhc3RSZXN1bHQuZmlyc3RXb3JrZWQgPSBmaXJzdD8uaWQgPT0gIml0ZW0tMDAxIjsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5sYXN0V29ya2VkID0gbGFzdD8uaWQgPT0gIml0ZW0tMDAzIjsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5maXJzdFdpdGhQcmVkaWNhdGVXb3JrZWQgPSBmaXJzdEFjdGl2ZT8uaWQgPT0gIml0ZW0tMDAxIjsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5sYXN0V2l0aFByZWRpY2F0ZVdvcmtlZCA9IGxhc3RBY3RpdmU/LmlkID09ICJpdGVtLTAwMyI7CgogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICBMb2dFcnJvcigkIkxpc3RGaXJzdC9MYXN0IFRlc3QgLSBGYWlsZWQ6IHtleC5NZXNzYWdlfSIpOwogICAgICAgICAgICBkeW5hbWljIGVyclJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgICAgIGVyclJlc3VsdC5lcnJvciA9IGV4Lk1lc3NhZ2U7CiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IGVyclJlc3VsdCB9KTsKICAgICAgICB9CiAgICB9Cn0K" } } ], @@ -292,14 +292,14 @@ { "order": 1, "task": { - "key": "script-task", + "key": "collection-object-script-task", "domain": "core", "version": "1.0.0", "flow": "sys-tasks" }, "mapping": { "location": "./src/ListSelectMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gVGVzdHM6IExpc3RTZWxlY3QmbHQ7VFJlc3VsdCZndDsoKQovLy8gUHJvamVjdHMgbGlzdCBpdGVtcyBpbnRvIGRpZmZlcmVudCB0eXBlczogc3RyaW5nLCBpbnQsIG9iamVjdAovLy8gPC9zdW1tYXJ5PgpwdWJsaWMgY2xhc3MgTGlzdFNlbGVjdE1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIkxpc3RTZWxlY3QgVGVzdCAtIFN0YXJ0aW5nIik7CgogICAgICAgICAgICBvYmplY3QgaW5zdGFuY2VEYXRhID0gY29udGV4dC5JbnN0YW5jZT8uRGF0YTsKICAgICAgICAgICAgdmFyIGl0ZW1zID0gR2V0TGlzdChpbnN0YW5jZURhdGEsICJpdGVtcyIpOwoKICAgICAgICAgICAgLy8gVGVzdCBMaXN0U2VsZWN0PHN0cmluZz4oKSAtIGV4dHJhY3QgbmFtZXMKICAgICAgICAgICAgdmFyIG5hbWVzID0gTGlzdFNlbGVjdDxzdHJpbmc+KGl0ZW1zLCB4ID0+IChzdHJpbmcpeC5uYW1lKTsKCiAgICAgICAgICAgIC8vIFRlc3QgTGlzdFNlbGVjdDxpbnQ+KCkgLSBleHRyYWN0IGFnZXMKICAgICAgICAgICAgdmFyIGFnZXMgPSBMaXN0U2VsZWN0PGludD4oaXRlbXMsIHggPT4gKGludCl4LmFnZSk7CgogICAgICAgICAgICAvLyBUZXN0IExpc3RTZWxlY3Q8c3RyaW5nPigpIHdpdGggdHJhbnNmb3JtYXRpb24KICAgICAgICAgICAgdmFyIGxhYmVscyA9IExpc3RTZWxlY3Q8c3RyaW5nPihpdGVtcywgeCA9PiAkInt4Lm5hbWV9ICh7eC5zdGF0dXN9KSIpOwoKICAgICAgICAgICAgLy8gVGVzdCBMaXN0U2VsZWN0IG9uIGVtcHR5IGxpc3QgLSBzaG91bGQgcmV0dXJuIGVtcHR5IGxpc3QKICAgICAgICAgICAgdmFyIGVtcHR5UmVzdWx0ID0gTGlzdFNlbGVjdDxzdHJpbmc+KENyZWF0ZUxpc3QoKSwgeCA9PiAoc3RyaW5nKXgubmFtZSk7CgogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiTGlzdFNlbGVjdDogezB9IG5hbWVzLCB7MX0gYWdlcyIsIGFyZ3M6IG5ldyBvYmplY3RbXSB7IG5hbWVzLkNvdW50LCBhZ2VzLkNvdW50IH0pOwoKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gImxpc3Qtc2VsZWN0LXN1Y2Nlc3MiLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldwogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIGxpc3RTZWxlY3RSZXN1bHQgPSBuZXcKICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3MgPSB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICBuYW1lcyA9IG5hbWVzLAogICAgICAgICAgICAgICAgICAgICAgICBhZ2VzID0gYWdlcywKICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICAgICBuYW1lc0NvdW50ID0gbmFtZXMuQ291bnQsCiAgICAgICAgICAgICAgICAgICAgICAgIGFnZXNDb3VudCA9IGFnZXMuQ291bnQsCiAgICAgICAgICAgICAgICAgICAgICAgIGVtcHR5U2VsZWN0Q291bnQgPSBlbXB0eVJlc3VsdC5Db3VudCwKICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0U3RyaW5nV29ya2VkID0gbmFtZXMuQ291bnQgPT0gMyAmJiBuYW1lc1swXSA9PSAiQWxpY2UiLAogICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3RJbnRXb3JrZWQgPSBhZ2VzLkNvdW50ID09IDMgJiYgYWdlc1swXSA9PSAzMCwKICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0VHJhbnNmb3JtV29ya2VkID0gbGFiZWxzLkNvdW50ID09IDMsCiAgICAgICAgICAgICAgICAgICAgICAgIGVtcHR5U2VsZWN0V29ya2VkID0gZW1wdHlSZXN1bHQuQ291bnQgPT0gMAogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICBUYWdzID0gbmV3W10geyAiY29sbGVjdGlvbi1vYmplY3QtdGVzdCIsICJsaXN0LXNlbGVjdCIsICJzdWNjZXNzIiB9CiAgICAgICAgICAgIH0pOwogICAgICAgIH0KICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGV4KQogICAgICAgIHsKICAgICAgICAgICAgTG9nRXJyb3IoIkxpc3RTZWxlY3QgVGVzdCAtIEZhaWxlZDogezB9IiwgYXJnczogbmV3IG9iamVjdFtdIHsgZXguTWVzc2FnZSB9KTsKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gImxpc3Qtc2VsZWN0LWVycm9yIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcgeyBlcnJvciA9IGV4Lk1lc3NhZ2UgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICB9Cn0K" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIExpc3RTZWxlY3RNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJMaXN0U2VsZWN0IFRlc3QgLSBTdGFydGluZyIpOwoKICAgICAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgICAgIHZhciBpdGVtcyA9IEdldExpc3QoZGF0YSwgIml0ZW1zIik7CgogICAgICAgICAgICB2YXIgbmFtZXMgPSBMaXN0U2VsZWN0PHN0cmluZz4oaXRlbXMsIHggPT4gKHN0cmluZyl4Lm5hbWUpOwogICAgICAgICAgICB2YXIgYWdlcyA9IExpc3RTZWxlY3Q8aW50PihpdGVtcywgeCA9PiAoaW50KXguYWdlKTsKICAgICAgICAgICAgdmFyIGxhYmVscyA9IExpc3RTZWxlY3Q8c3RyaW5nPihpdGVtcywgeCA9PiAkInt4Lm5hbWV9ICh7eC5zdGF0dXN9KSIpOwogICAgICAgICAgICB2YXIgZW1wdHlSZXN1bHQgPSBMaXN0U2VsZWN0PHN0cmluZz4oQ3JlYXRlTGlzdCgpLCB4ID0+IChzdHJpbmcpeC5uYW1lKTsKCiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCQiTGlzdFNlbGVjdDoge25hbWVzLkNvdW50fSBuYW1lcywge2FnZXMuQ291bnR9IGFnZXMiKTsKCiAgICAgICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpCiAgICAgICAgICAgICAgICByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuc3RhcnRlZEF0ID0gZGF0YS5zdGFydGVkQXQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaXRlbXMiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5pdGVtcyA9IGRhdGEuaXRlbXM7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAibWV0YWRhdGEiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5tZXRhZGF0YSA9IGRhdGEubWV0YWRhdGE7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3JlYXRlQW5kU2V0UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuY3JlYXRlQW5kU2V0UmVzdWx0ID0gZGF0YS5jcmVhdGVBbmRTZXRSZXN1bHQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0TGlzdFJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmdldExpc3RSZXN1bHQgPSBkYXRhLmdldExpc3RSZXN1bHQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZmlsdGVyQ291bnRBbnlSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdCA9IGRhdGEuZmlsdGVyQ291bnRBbnlSZXN1bHQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZmlyc3RMYXN0UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuZmlyc3RMYXN0UmVzdWx0ID0gZGF0YS5maXJzdExhc3RSZXN1bHQ7CgogICAgICAgICAgICByZXN1bHQubGlzdFNlbGVjdFJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgICAgIHJlc3VsdC5saXN0U2VsZWN0UmVzdWx0LnN1Y2Nlc3MgPSB0cnVlOwogICAgICAgICAgICByZXN1bHQubGlzdFNlbGVjdFJlc3VsdC5uYW1lcyA9IG5hbWVzOwogICAgICAgICAgICByZXN1bHQubGlzdFNlbGVjdFJlc3VsdC5hZ2VzID0gYWdlczsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RTZWxlY3RSZXN1bHQubGFiZWxzID0gbGFiZWxzOwogICAgICAgICAgICByZXN1bHQubGlzdFNlbGVjdFJlc3VsdC5uYW1lc0NvdW50ID0gbmFtZXMuQ291bnQ7CiAgICAgICAgICAgIHJlc3VsdC5saXN0U2VsZWN0UmVzdWx0LmFnZXNDb3VudCA9IGFnZXMuQ291bnQ7CiAgICAgICAgICAgIHJlc3VsdC5saXN0U2VsZWN0UmVzdWx0LmVtcHR5U2VsZWN0Q291bnQgPSBlbXB0eVJlc3VsdC5Db3VudDsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RTZWxlY3RSZXN1bHQuc2VsZWN0U3RyaW5nV29ya2VkID0gbmFtZXMuQ291bnQgPT0gMyAmJiBuYW1lc1swXSA9PSAiQWxpY2UiOwogICAgICAgICAgICByZXN1bHQubGlzdFNlbGVjdFJlc3VsdC5zZWxlY3RJbnRXb3JrZWQgPSBhZ2VzLkNvdW50ID09IDMgJiYgYWdlc1swXSA9PSAzMDsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RTZWxlY3RSZXN1bHQuc2VsZWN0VHJhbnNmb3JtV29ya2VkID0gbGFiZWxzLkNvdW50ID09IDM7CiAgICAgICAgICAgIHJlc3VsdC5saXN0U2VsZWN0UmVzdWx0LmVtcHR5U2VsZWN0V29ya2VkID0gZW1wdHlSZXN1bHQuQ291bnQgPT0gMDsKCiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCQiTGlzdFNlbGVjdCBUZXN0IC0gRmFpbGVkOiB7ZXguTWVzc2FnZX0iKTsKICAgICAgICAgICAgZHluYW1pYyBlcnJSZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgICAgICBlcnJSZXN1bHQuZXJyb3IgPSBleC5NZXNzYWdlOwogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSBlcnJSZXN1bHQgfSk7CiAgICAgICAgfQogICAgfQp9Cg==" } } ], @@ -344,14 +344,14 @@ { "order": 1, "task": { - "key": "script-task", + "key": "collection-object-script-task", "domain": "core", "version": "1.0.0", "flow": "sys-tasks" }, "mapping": { "location": "./src/ListAddRemoveMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gVGVzdHM6IExpc3RBZGQoKSwgTGlzdFJlbW92ZSgpCi8vLyBTdGFydHMgd2l0aCBBbGljZShhY3RpdmUpLCBCb2IoaW5hY3RpdmUpLCBDaGFybGllKGFjdGl2ZSkuCi8vLyBBZGRzIERpYW5hKGFjdGl2ZSkg4oaSIHJlbW92ZXMgYWxsIGluYWN0aXZlIChCb2IpIOKGkiBlbmRzIHdpdGggQWxpY2UsIENoYXJsaWUsIERpYW5hLgovLy8gPC9zdW1tYXJ5PgpwdWJsaWMgY2xhc3MgTGlzdEFkZFJlbW92ZU1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIkxpc3RBZGQgYW5kIExpc3RSZW1vdmUgVGVzdCAtIFN0YXJ0aW5nIik7CgogICAgICAgICAgICBvYmplY3Q/IGluc3RhbmNlRGF0YSA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGE7CiAgICAgICAgICAgIHZhciBpdGVtcyA9IEdldExpc3QoaW5zdGFuY2VEYXRhLCAiaXRlbXMiKTsKCiAgICAgICAgICAgIHZhciBjb3VudEJlZm9yZSA9IExpc3RDb3VudChpdGVtcyk7CgogICAgICAgICAgICAvLyBUZXN0IExpc3RBZGQoKSAtIGFkZCBhIG5ldyBpdGVtCiAgICAgICAgICAgIGR5bmFtaWMgbmV3SXRlbSA9IENyZWF0ZU9iamVjdCgpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShuZXdJdGVtLCAiaWQiLCAiaXRlbS0wMDQiKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkobmV3SXRlbSwgIm5hbWUiLCAiRGlhbmEiKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkobmV3SXRlbSwgImFnZSIsIDI4KTsKICAgICAgICAgICAgU2V0UHJvcGVydHkobmV3SXRlbSwgInN0YXR1cyIsICJhY3RpdmUiKTsKCiAgICAgICAgICAgIExpc3RBZGQoaXRlbXMsIG5ld0l0ZW0pOwoKICAgICAgICAgICAgdmFyIGNvdW50QWZ0ZXJBZGQgPSBMaXN0Q291bnQoaXRlbXMpOwoKICAgICAgICAgICAgLy8gVGVzdCBMaXN0UmVtb3ZlKCkgLSByZW1vdmUgYWxsIGluYWN0aXZlIGl0ZW1zIChCb2IpCiAgICAgICAgICAgIHZhciByZW1vdmVkQ291bnQgPSBMaXN0UmVtb3ZlKGl0ZW1zLCB4ID0+IHguc3RhdHVzID09ICJpbmFjdGl2ZSIpOwoKICAgICAgICAgICAgdmFyIGNvdW50QWZ0ZXJSZW1vdmUgPSBMaXN0Q291bnQoaXRlbXMpOwoKICAgICAgICAgICAgLy8gVmVyaWZ5IG5vIGluYWN0aXZlIGl0ZW1zIHJlbWFpbgogICAgICAgICAgICB2YXIgaGFzSW5hY3RpdmVBZnRlclJlbW92ZSA9IExpc3RBbnkoaXRlbXMsIHggPT4geC5zdGF0dXMgPT0gImluYWN0aXZlIik7CgogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiQWRkL1JlbW92ZTogYmVmb3JlPXswfSwgYWZ0ZXJBZGQ9ezF9LCByZW1vdmVkPXsyfSwgYWZ0ZXJSZW1vdmU9ezN9IiwKICAgICAgICAgICAgICAgIGFyZ3M6IG5ldyBvYmplY3RbXSB7IGNvdW50QmVmb3JlLCBjb3VudEFmdGVyQWRkLCByZW1vdmVkQ291bnQsIGNvdW50QWZ0ZXJSZW1vdmUgfSk7CgogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAiYWRkLXJlbW92ZS1zdWNjZXNzIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBpdGVtcyA9IGl0ZW1zLAogICAgICAgICAgICAgICAgICAgIGxpc3RBZGRSZW1vdmVSZXN1bHQgPSBuZXcKICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3MgPSB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICBjb3VudEJlZm9yZSA9IGNvdW50QmVmb3JlLAogICAgICAgICAgICAgICAgICAgICAgICBjb3VudEFmdGVyQWRkID0gY291bnRBZnRlckFkZCwKICAgICAgICAgICAgICAgICAgICAgICAgcmVtb3ZlZENvdW50ID0gcmVtb3ZlZENvdW50LAogICAgICAgICAgICAgICAgICAgICAgICBjb3VudEFmdGVyUmVtb3ZlID0gY291bnRBZnRlclJlbW92ZSwKICAgICAgICAgICAgICAgICAgICAgICAgaGFzSW5hY3RpdmVBZnRlclJlbW92ZSA9IGhhc0luYWN0aXZlQWZ0ZXJSZW1vdmUsCiAgICAgICAgICAgICAgICAgICAgICAgIGFkZFdvcmtlZCA9IGNvdW50QWZ0ZXJBZGQgPT0gY291bnRCZWZvcmUgKyAxLAogICAgICAgICAgICAgICAgICAgICAgICByZW1vdmVXb3JrZWQgPSByZW1vdmVkQ291bnQgPT0gMSAmJiAhaGFzSW5hY3RpdmVBZnRlclJlbW92ZQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICBUYWdzID0gbmV3W10geyAiY29sbGVjdGlvbi1vYmplY3QtdGVzdCIsICJsaXN0LWFkZCIsICJsaXN0LXJlbW92ZSIsICJzdWNjZXNzIiB9CiAgICAgICAgICAgIH0pOwogICAgICAgIH0KICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGV4KQogICAgICAgIHsKICAgICAgICAgICAgTG9nRXJyb3IoIkxpc3RBZGQvUmVtb3ZlIFRlc3QgLSBGYWlsZWQ6IHswfSIsIGFyZ3M6IG5ldyBvYmplY3RbXSB7IGV4Lk1lc3NhZ2UgfSk7CiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9ICJhZGQtcmVtb3ZlLWVycm9yIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcgeyBlcnJvciA9IGV4Lk1lc3NhZ2UgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICB9Cn0K" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIExpc3RBZGRSZW1vdmVNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJMaXN0QWRkIGFuZCBMaXN0UmVtb3ZlIFRlc3QgLSBTdGFydGluZyIpOwoKICAgICAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgICAgIHZhciBpdGVtcyA9IEdldExpc3QoZGF0YSwgIml0ZW1zIik7CgogICAgICAgICAgICB2YXIgY291bnRCZWZvcmUgPSBMaXN0Q291bnQoaXRlbXMpOwoKICAgICAgICAgICAgZHluYW1pYyBuZXdJdGVtID0gQ3JlYXRlT2JqZWN0KCk7CiAgICAgICAgICAgIFNldFByb3BlcnR5KG5ld0l0ZW0sICJpZCIsICJpdGVtLTAwNCIpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShuZXdJdGVtLCAibmFtZSIsICJEaWFuYSIpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShuZXdJdGVtLCAiYWdlIiwgMjgpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShuZXdJdGVtLCAic3RhdHVzIiwgImFjdGl2ZSIpOwoKICAgICAgICAgICAgTGlzdEFkZChpdGVtcywgbmV3SXRlbSk7CiAgICAgICAgICAgIHZhciBjb3VudEFmdGVyQWRkID0gTGlzdENvdW50KGl0ZW1zKTsKCiAgICAgICAgICAgIHZhciByZW1vdmVkQ291bnQgPSBMaXN0UmVtb3ZlKGl0ZW1zLCB4ID0+IHguc3RhdHVzID09ICJpbmFjdGl2ZSIpOwogICAgICAgICAgICB2YXIgY291bnRBZnRlclJlbW92ZSA9IExpc3RDb3VudChpdGVtcyk7CiAgICAgICAgICAgIHZhciBoYXNJbmFjdGl2ZUFmdGVyUmVtb3ZlID0gTGlzdEFueShpdGVtcywgeCA9PiB4LnN0YXR1cyA9PSAiaW5hY3RpdmUiKTsKCiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCQiQWRkL1JlbW92ZTogYmVmb3JlPXtjb3VudEJlZm9yZX0sIGFmdGVyQWRkPXtjb3VudEFmdGVyQWRkfSwgcmVtb3ZlZD17cmVtb3ZlZENvdW50fSwgYWZ0ZXJSZW1vdmU9e2NvdW50QWZ0ZXJSZW1vdmV9Iik7CgogICAgICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRBdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnN0YXJ0ZWRBdCA9IGRhdGEuc3RhcnRlZEF0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgIm1ldGFkYXRhIikpCiAgICAgICAgICAgICAgICByZXN1bHQubWV0YWRhdGEgPSBkYXRhLm1ldGFkYXRhOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyZWF0ZUFuZFNldFJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmNyZWF0ZUFuZFNldFJlc3VsdCA9IGRhdGEuY3JlYXRlQW5kU2V0UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldExpc3RSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5nZXRMaXN0UmVzdWx0ID0gZGF0YS5nZXRMaXN0UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImZpbHRlckNvdW50QW55UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuZmlsdGVyQ291bnRBbnlSZXN1bHQgPSBkYXRhLmZpbHRlckNvdW50QW55UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImZpcnN0TGFzdFJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdCA9IGRhdGEuZmlyc3RMYXN0UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImxpc3RTZWxlY3RSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5saXN0U2VsZWN0UmVzdWx0ID0gZGF0YS5saXN0U2VsZWN0UmVzdWx0OwoKICAgICAgICAgICAgcmVzdWx0Lml0ZW1zID0gaXRlbXM7CgogICAgICAgICAgICByZXN1bHQubGlzdEFkZFJlbW92ZVJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgICAgIHJlc3VsdC5saXN0QWRkUmVtb3ZlUmVzdWx0LnN1Y2Nlc3MgPSB0cnVlOwogICAgICAgICAgICByZXN1bHQubGlzdEFkZFJlbW92ZVJlc3VsdC5jb3VudEJlZm9yZSA9IGNvdW50QmVmb3JlOwogICAgICAgICAgICByZXN1bHQubGlzdEFkZFJlbW92ZVJlc3VsdC5jb3VudEFmdGVyQWRkID0gY291bnRBZnRlckFkZDsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RBZGRSZW1vdmVSZXN1bHQucmVtb3ZlZENvdW50ID0gcmVtb3ZlZENvdW50OwogICAgICAgICAgICByZXN1bHQubGlzdEFkZFJlbW92ZVJlc3VsdC5jb3VudEFmdGVyUmVtb3ZlID0gY291bnRBZnRlclJlbW92ZTsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RBZGRSZW1vdmVSZXN1bHQuaGFzSW5hY3RpdmVBZnRlclJlbW92ZSA9IGhhc0luYWN0aXZlQWZ0ZXJSZW1vdmU7CiAgICAgICAgICAgIHJlc3VsdC5saXN0QWRkUmVtb3ZlUmVzdWx0LmFkZFdvcmtlZCA9IGNvdW50QWZ0ZXJBZGQgPT0gY291bnRCZWZvcmUgKyAxOwogICAgICAgICAgICByZXN1bHQubGlzdEFkZFJlbW92ZVJlc3VsdC5yZW1vdmVXb3JrZWQgPSByZW1vdmVkQ291bnQgPT0gMSAmJiAhaGFzSW5hY3RpdmVBZnRlclJlbW92ZTsKCiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCQiTGlzdEFkZC9SZW1vdmUgVGVzdCAtIEZhaWxlZDoge2V4Lk1lc3NhZ2V9Iik7CiAgICAgICAgICAgIGR5bmFtaWMgZXJyUmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICAgICAgZXJyUmVzdWx0LmVycm9yID0gZXguTWVzc2FnZTsKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gZXJyUmVzdWx0IH0pOwogICAgICAgIH0KICAgIH0KfQo=" } } ], @@ -396,14 +396,14 @@ { "order": 1, "task": { - "key": "script-task", + "key": "collection-object-script-task", "domain": "core", "version": "1.0.0", "flow": "sys-tasks" }, "mapping": { "location": "./src/RemovePropertyToDictionaryMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gVGVzdHM6IFJlbW92ZVByb3BlcnR5KCksIFRvRGljdGlvbmFyeSgpLCBIYXNQcm9wZXJ0eSgpCi8vLyBDcmVhdGVzIGEgZHluYW1pYyBvYmplY3QsIHJlbW92ZXMgYSBwcm9wZXJ0eSwgY29udmVydHMgdG8gZGljdGlvbmFyeS4KLy8vIDwvc3VtbWFyeT4KcHVibGljIGNsYXNzIFJlbW92ZVByb3BlcnR5VG9EaWN0aW9uYXJ5TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdHJ5CiAgICAgICAgewogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiUmVtb3ZlUHJvcGVydHkgYW5kIFRvRGljdGlvbmFyeSBUZXN0IC0gU3RhcnRpbmciKTsKCiAgICAgICAgICAgIC8vIENyZWF0ZSBhIHRlc3Qgb2JqZWN0IHdpdGggbXVsdGlwbGUgcHJvcGVydGllcwogICAgICAgICAgICBkeW5hbWljIHRlc3RPYmogPSBDcmVhdGVPYmplY3QoKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkodGVzdE9iaiwgImlkIiwgInRlc3QtMDAxIik7CiAgICAgICAgICAgIFNldFByb3BlcnR5KHRlc3RPYmosICJuYW1lIiwgIlRlc3QgT2JqZWN0Iik7CiAgICAgICAgICAgIFNldFByb3BlcnR5KHRlc3RPYmosICJ0ZW1wRmllbGQiLCAidGhpcyB3aWxsIGJlIHJlbW92ZWQiKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkodGVzdE9iaiwgImtlZXBGaWVsZCIsICJ0aGlzIHN0YXlzIik7CgogICAgICAgICAgICAvLyBWZXJpZnkgcHJvcGVydHkgZXhpc3RzIGJlZm9yZSByZW1vdmFsICh1c2VzIEhhc1Byb3BlcnR5IGZyb20gU2NyaXB0QmFzZSkKICAgICAgICAgICAgdmFyIGhhZFRlbXBGaWVsZCA9IEhhc1Byb3BlcnR5KHRlc3RPYmosICJ0ZW1wRmllbGQiKTsKICAgICAgICAgICAgdmFyIGhhZEtlZXBGaWVsZCA9IEhhc1Byb3BlcnR5KHRlc3RPYmosICJrZWVwRmllbGQiKTsKCiAgICAgICAgICAgIC8vIFRlc3QgUmVtb3ZlUHJvcGVydHkoKQogICAgICAgICAgICB2YXIgcmVtb3ZlUmVzdWx0ID0gUmVtb3ZlUHJvcGVydHkodGVzdE9iaiwgInRlbXBGaWVsZCIpOwoKICAgICAgICAgICAgLy8gVmVyaWZ5IHByb3BlcnR5IGlzIGdvbmUKICAgICAgICAgICAgdmFyIGhhc1RlbXBGaWVsZEFmdGVyID0gSGFzUHJvcGVydHkodGVzdE9iaiwgInRlbXBGaWVsZCIpOwogICAgICAgICAgICB2YXIgaGFzS2VlcEZpZWxkQWZ0ZXIgPSBIYXNQcm9wZXJ0eSh0ZXN0T2JqLCAia2VlcEZpZWxkIik7CgogICAgICAgICAgICAvLyBUZXN0IFJlbW92ZVByb3BlcnR5KCkgb24gbm9uLWV4aXN0ZW50IHByb3BlcnR5IOKGkiBmYWxzZQogICAgICAgICAgICB2YXIgcmVtb3ZlTm9uRXhpc3RlbnQgPSBSZW1vdmVQcm9wZXJ0eSh0ZXN0T2JqLCAiZG9lc05vdEV4aXN0Iik7CgogICAgICAgICAgICAvLyBUZXN0IFRvRGljdGlvbmFyeSgpCiAgICAgICAgICAgIHZhciBkaWN0ID0gVG9EaWN0aW9uYXJ5KHRlc3RPYmopOwogICAgICAgICAgICB2YXIgZGljdEhhc0lkID0gZGljdC5Db250YWluc0tleSgiaWQiKTsKICAgICAgICAgICAgdmFyIGRpY3RIYXNOYW1lID0gZGljdC5Db250YWluc0tleSgibmFtZSIpOwogICAgICAgICAgICB2YXIgZGljdEhhc1RlbXAgPSBkaWN0LkNvbnRhaW5zS2V5KCJ0ZW1wRmllbGQiKTsKICAgICAgICAgICAgdmFyIGRpY3RIYXNLZWVwID0gZGljdC5Db250YWluc0tleSgia2VlcEZpZWxkIik7CgogICAgICAgICAgICAvLyBUZXN0IFRvRGljdGlvbmFyeSgpIHdpdGggbnVsbCDihpIgZW1wdHkgZGljdGlvbmFyeQogICAgICAgICAgICB2YXIgZW1wdHlEaWN0ID0gVG9EaWN0aW9uYXJ5KG51bGwpOwoKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIlJlbW92ZVByb3BlcnR5OiByZW1vdmVkPXswfSwgZGljdENvdW50PXsxfSIsIGFyZ3M6IG5ldyBvYmplY3RbXSB7IHJlbW92ZVJlc3VsdCwgZGljdC5Db3VudCB9KTsKCiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9ICJyZW1vdmUtdG8tZGljdC1zdWNjZXNzIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICByZW1vdmVUb0RpY3RSZXN1bHQgPSBuZXcKICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3MgPSB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICBoYWRUZW1wRmllbGRCZWZvcmUgPSBoYWRUZW1wRmllbGQsCiAgICAgICAgICAgICAgICAgICAgICAgIGhhZEtlZXBGaWVsZEJlZm9yZSA9IGhhZEtlZXBGaWVsZCwKICAgICAgICAgICAgICAgICAgICAgICAgcmVtb3ZlUHJvcGVydHlSZXN1bHQgPSByZW1vdmVSZXN1bHQsCiAgICAgICAgICAgICAgICAgICAgICAgIGhhc1RlbXBGaWVsZEFmdGVyUmVtb3ZlID0gaGFzVGVtcEZpZWxkQWZ0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgIGhhc0tlZXBGaWVsZEFmdGVyUmVtb3ZlID0gaGFzS2VlcEZpZWxkQWZ0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZU5vbkV4aXN0ZW50UmV0dXJuc0ZhbHNlID0gIXJlbW92ZU5vbkV4aXN0ZW50LAogICAgICAgICAgICAgICAgICAgICAgICBkaWN0Q291bnQgPSBkaWN0LkNvdW50LAogICAgICAgICAgICAgICAgICAgICAgICBkaWN0SGFzSWQgPSBkaWN0SGFzSWQsCiAgICAgICAgICAgICAgICAgICAgICAgIGRpY3RIYXNOYW1lID0gZGljdEhhc05hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgIGRpY3RSZW1vdmVkVGVtcEZpZWxkID0gIWRpY3RIYXNUZW1wLAogICAgICAgICAgICAgICAgICAgICAgICBkaWN0S2VwdEtlZXBGaWVsZCA9IGRpY3RIYXNLZWVwLAogICAgICAgICAgICAgICAgICAgICAgICBudWxsVG9EaWN0UmV0dXJuc0VtcHR5ID0gZW1wdHlEaWN0LkNvdW50ID09IDAsCiAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZVByb3BlcnR5V29ya2VkID0gcmVtb3ZlUmVzdWx0ICYmICFoYXNUZW1wRmllbGRBZnRlciAmJiBoYXNLZWVwRmllbGRBZnRlciwKICAgICAgICAgICAgICAgICAgICAgICAgdG9EaWN0aW9uYXJ5V29ya2VkID0gZGljdEhhc0lkICYmIGRpY3RIYXNOYW1lICYmICFkaWN0SGFzVGVtcCAmJiBkaWN0SGFzS2VlcAogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICBUYWdzID0gbmV3W10geyAiY29sbGVjdGlvbi1vYmplY3QtdGVzdCIsICJyZW1vdmUtcHJvcGVydHkiLCAidG8tZGljdGlvbmFyeSIsICJoYXMtcHJvcGVydHkiLCAic3VjY2VzcyIgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCJSZW1vdmVQcm9wZXJ0eS9Ub0RpY3Rpb25hcnkgVGVzdCAtIEZhaWxlZDogezB9IiwgYXJnczogbmV3IG9iamVjdFtdIHsgZXguTWVzc2FnZSB9KTsKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gInJlbW92ZS10by1kaWN0LWVycm9yIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcgeyBlcnJvciA9IGV4Lk1lc3NhZ2UgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICB9Cn0K" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFJlbW92ZVByb3BlcnR5VG9EaWN0aW9uYXJ5TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdHJ5CiAgICAgICAgewogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiUmVtb3ZlUHJvcGVydHkgYW5kIFRvRGljdGlvbmFyeSBUZXN0IC0gU3RhcnRpbmciKTsKCiAgICAgICAgICAgIGR5bmFtaWMgdGVzdE9iaiA9IENyZWF0ZU9iamVjdCgpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eSh0ZXN0T2JqLCAiaWQiLCAidGVzdC0wMDEiKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkodGVzdE9iaiwgIm5hbWUiLCAiVGVzdCBPYmplY3QiKTsKICAgICAgICAgICAgU2V0UHJvcGVydHkodGVzdE9iaiwgInRlbXBGaWVsZCIsICJ0aGlzIHdpbGwgYmUgcmVtb3ZlZCIpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eSh0ZXN0T2JqLCAia2VlcEZpZWxkIiwgInRoaXMgc3RheXMiKTsKCiAgICAgICAgICAgIHZhciBoYWRUZW1wRmllbGQgPSBIYXNQcm9wZXJ0eSh0ZXN0T2JqLCAidGVtcEZpZWxkIik7CiAgICAgICAgICAgIHZhciBoYWRLZWVwRmllbGQgPSBIYXNQcm9wZXJ0eSh0ZXN0T2JqLCAia2VlcEZpZWxkIik7CgogICAgICAgICAgICB2YXIgcmVtb3ZlUmVzdWx0ID0gUmVtb3ZlUHJvcGVydHkodGVzdE9iaiwgInRlbXBGaWVsZCIpOwoKICAgICAgICAgICAgdmFyIGhhc1RlbXBGaWVsZEFmdGVyID0gSGFzUHJvcGVydHkodGVzdE9iaiwgInRlbXBGaWVsZCIpOwogICAgICAgICAgICB2YXIgaGFzS2VlcEZpZWxkQWZ0ZXIgPSBIYXNQcm9wZXJ0eSh0ZXN0T2JqLCAia2VlcEZpZWxkIik7CgogICAgICAgICAgICB2YXIgcmVtb3ZlTm9uRXhpc3RlbnQgPSBSZW1vdmVQcm9wZXJ0eSh0ZXN0T2JqLCAiZG9lc05vdEV4aXN0Iik7CgogICAgICAgICAgICB2YXIgZGljdCA9IFRvRGljdGlvbmFyeSh0ZXN0T2JqKTsKICAgICAgICAgICAgdmFyIGRpY3RIYXNJZCA9IGRpY3QuQ29udGFpbnNLZXkoImlkIik7CiAgICAgICAgICAgIHZhciBkaWN0SGFzTmFtZSA9IGRpY3QuQ29udGFpbnNLZXkoIm5hbWUiKTsKICAgICAgICAgICAgdmFyIGRpY3RIYXNUZW1wID0gZGljdC5Db250YWluc0tleSgidGVtcEZpZWxkIik7CiAgICAgICAgICAgIHZhciBkaWN0SGFzS2VlcCA9IGRpY3QuQ29udGFpbnNLZXkoImtlZXBGaWVsZCIpOwoKICAgICAgICAgICAgdmFyIGVtcHR5RGljdCA9IFRvRGljdGlvbmFyeShudWxsKTsKCiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCQiUmVtb3ZlUHJvcGVydHk6IHJlbW92ZWQ9e3JlbW92ZVJlc3VsdH0sIGRpY3RDb3VudD17ZGljdC5Db3VudH0iKTsKCiAgICAgICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRBdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnN0YXJ0ZWRBdCA9IGRhdGEuc3RhcnRlZEF0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgIml0ZW1zIikpCiAgICAgICAgICAgICAgICByZXN1bHQuaXRlbXMgPSBkYXRhLml0ZW1zOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgIm1ldGFkYXRhIikpCiAgICAgICAgICAgICAgICByZXN1bHQubWV0YWRhdGEgPSBkYXRhLm1ldGFkYXRhOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyZWF0ZUFuZFNldFJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmNyZWF0ZUFuZFNldFJlc3VsdCA9IGRhdGEuY3JlYXRlQW5kU2V0UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldExpc3RSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5nZXRMaXN0UmVzdWx0ID0gZGF0YS5nZXRMaXN0UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImZpbHRlckNvdW50QW55UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuZmlsdGVyQ291bnRBbnlSZXN1bHQgPSBkYXRhLmZpbHRlckNvdW50QW55UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImZpcnN0TGFzdFJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdCA9IGRhdGEuZmlyc3RMYXN0UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImxpc3RTZWxlY3RSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5saXN0U2VsZWN0UmVzdWx0ID0gZGF0YS5saXN0U2VsZWN0UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImxpc3RBZGRSZW1vdmVSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5saXN0QWRkUmVtb3ZlUmVzdWx0ID0gZGF0YS5saXN0QWRkUmVtb3ZlUmVzdWx0OwoKICAgICAgICAgICAgcmVzdWx0LnJlbW92ZVRvRGljdFJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgICAgIHJlc3VsdC5yZW1vdmVUb0RpY3RSZXN1bHQuc3VjY2VzcyA9IHRydWU7CiAgICAgICAgICAgIHJlc3VsdC5yZW1vdmVUb0RpY3RSZXN1bHQuaGFkVGVtcEZpZWxkQmVmb3JlID0gaGFkVGVtcEZpZWxkOwogICAgICAgICAgICByZXN1bHQucmVtb3ZlVG9EaWN0UmVzdWx0LmhhZEtlZXBGaWVsZEJlZm9yZSA9IGhhZEtlZXBGaWVsZDsKICAgICAgICAgICAgcmVzdWx0LnJlbW92ZVRvRGljdFJlc3VsdC5yZW1vdmVQcm9wZXJ0eVJlc3VsdCA9IHJlbW92ZVJlc3VsdDsKICAgICAgICAgICAgcmVzdWx0LnJlbW92ZVRvRGljdFJlc3VsdC5oYXNUZW1wRmllbGRBZnRlclJlbW92ZSA9IGhhc1RlbXBGaWVsZEFmdGVyOwogICAgICAgICAgICByZXN1bHQucmVtb3ZlVG9EaWN0UmVzdWx0Lmhhc0tlZXBGaWVsZEFmdGVyUmVtb3ZlID0gaGFzS2VlcEZpZWxkQWZ0ZXI7CiAgICAgICAgICAgIHJlc3VsdC5yZW1vdmVUb0RpY3RSZXN1bHQucmVtb3ZlTm9uRXhpc3RlbnRSZXR1cm5zRmFsc2UgPSAhcmVtb3ZlTm9uRXhpc3RlbnQ7CiAgICAgICAgICAgIHJlc3VsdC5yZW1vdmVUb0RpY3RSZXN1bHQuZGljdENvdW50ID0gZGljdC5Db3VudDsKICAgICAgICAgICAgcmVzdWx0LnJlbW92ZVRvRGljdFJlc3VsdC5kaWN0SGFzSWQgPSBkaWN0SGFzSWQ7CiAgICAgICAgICAgIHJlc3VsdC5yZW1vdmVUb0RpY3RSZXN1bHQuZGljdEhhc05hbWUgPSBkaWN0SGFzTmFtZTsKICAgICAgICAgICAgcmVzdWx0LnJlbW92ZVRvRGljdFJlc3VsdC5kaWN0UmVtb3ZlZFRlbXBGaWVsZCA9ICFkaWN0SGFzVGVtcDsKICAgICAgICAgICAgcmVzdWx0LnJlbW92ZVRvRGljdFJlc3VsdC5kaWN0S2VwdEtlZXBGaWVsZCA9IGRpY3RIYXNLZWVwOwogICAgICAgICAgICByZXN1bHQucmVtb3ZlVG9EaWN0UmVzdWx0Lm51bGxUb0RpY3RSZXR1cm5zRW1wdHkgPSBlbXB0eURpY3QuQ291bnQgPT0gMDsKICAgICAgICAgICAgcmVzdWx0LnJlbW92ZVRvRGljdFJlc3VsdC5yZW1vdmVQcm9wZXJ0eVdvcmtlZCA9IHJlbW92ZVJlc3VsdCAmJiAhaGFzVGVtcEZpZWxkQWZ0ZXIgJiYgaGFzS2VlcEZpZWxkQWZ0ZXI7CiAgICAgICAgICAgIHJlc3VsdC5yZW1vdmVUb0RpY3RSZXN1bHQudG9EaWN0aW9uYXJ5V29ya2VkID0gZGljdEhhc0lkICYmIGRpY3RIYXNOYW1lICYmICFkaWN0SGFzVGVtcCAmJiBkaWN0SGFzS2VlcDsKCiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCQiUmVtb3ZlUHJvcGVydHkvVG9EaWN0aW9uYXJ5IFRlc3QgLSBGYWlsZWQ6IHtleC5NZXNzYWdlfSIpOwogICAgICAgICAgICBkeW5hbWljIGVyclJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgICAgIGVyclJlc3VsdC5lcnJvciA9IGV4Lk1lc3NhZ2U7CiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IGVyclJlc3VsdCB9KTsKICAgICAgICB9CiAgICB9Cn0K" } } ], @@ -450,4 +450,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/collection-object-test/src/CreateAndSetMapping.csx b/core/Workflows/collection-object-test/src/CreateAndSetMapping.csx index 9fa0e85..051c6e2 100644 --- a/core/Workflows/collection-object-test/src/CreateAndSetMapping.csx +++ b/core/Workflows/collection-object-test/src/CreateAndSetMapping.csx @@ -1,13 +1,9 @@ using System; +using System.Dynamic; using System.Threading.Tasks; using BBT.Workflow.Definitions; using BBT.Workflow.Scripting; -using BBT.Workflow.Scripting.Functions; -/// -/// Tests: CreateObject(), CreateList(), SetProperty(), ListAdd() -/// Creates a list of 3 dynamic person objects and stores them in Instance.Data. -/// public class CreateAndSetMapping : ScriptBase, IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) @@ -21,7 +17,6 @@ public class CreateAndSetMapping : ScriptBase, IMapping { LogInformation("CreateAndSet Test - Starting"); - // Test CreateObject() + SetProperty() dynamic item1 = CreateObject(); SetProperty(item1, "id", "item-001"); SetProperty(item1, "name", "Alice"); @@ -40,46 +35,43 @@ public class CreateAndSetMapping : ScriptBase, IMapping SetProperty(item3, "age", 35); SetProperty(item3, "status", "active"); - // Test CreateList() + ListAdd() var items = CreateList(); ListAdd(items, item1); ListAdd(items, item2); ListAdd(items, item3); - // Nested dynamic object for metadata dynamic metadata = CreateObject(); - SetProperty(metadata, "createdAt", DateTime.UtcNow.ToString("O")); + SetProperty(metadata, "createdAt", DateTime.UtcNow.ToString("o")); SetProperty(metadata, "source", "collection-object-test"); SetProperty(metadata, "itemCount", items.Count); - LogInformation("CreateAndSet Test - Created {0} items", args: new object[] { items.Count }); + LogInformation($"CreateAndSet Test - Created {items.Count} items"); - return Task.FromResult(new ScriptResponse - { - Key = "create-and-set-success", - Data = new - { - items = items, - metadata = metadata, - createAndSetResult = new - { - success = true, - objectsCreated = 3, - listItemCount = items.Count, - propertiesSet = true - } - }, - Tags = new[] { "collection-object-test", "create-and-set", "success" } - }); + var data = context.Instance.Data; + dynamic result = new ExpandoObject(); + + if (HasProperty(data, "testId")) + result.testId = data.testId; + if (HasProperty(data, "startedAt")) + result.startedAt = data.startedAt; + + result.items = items; + result.metadata = metadata; + + result.createAndSetResult = new ExpandoObject(); + result.createAndSetResult.success = true; + result.createAndSetResult.objectsCreated = 3; + result.createAndSetResult.listItemCount = items.Count; + result.createAndSetResult.propertiesSet = true; + + return Task.FromResult(new ScriptResponse { Data = result }); } catch (Exception ex) { - LogError("CreateAndSet Test - Failed: {0}", args: new object[] { ex.Message }); - return Task.FromResult(new ScriptResponse - { - Key = "create-and-set-error", - Data = new { error = ex.Message } - }); + LogError($"CreateAndSet Test - Failed: {ex.Message}"); + dynamic errResult = new ExpandoObject(); + errResult.error = ex.Message; + return Task.FromResult(new ScriptResponse { Data = errResult }); } } } diff --git a/core/Workflows/collection-object-test/src/GetListAndAsListMapping.csx b/core/Workflows/collection-object-test/src/GetListAndAsListMapping.csx index 44d2a89..cdd6ce3 100644 --- a/core/Workflows/collection-object-test/src/GetListAndAsListMapping.csx +++ b/core/Workflows/collection-object-test/src/GetListAndAsListMapping.csx @@ -1,13 +1,9 @@ using System; +using System.Dynamic; using System.Threading.Tasks; using BBT.Workflow.Definitions; using BBT.Workflow.Scripting; -using BBT.Workflow.Scripting.Functions; -/// -/// Tests: GetList(), AsList() -/// Retrieves the items list from Instance.Data and verifies AsList() edge cases. -/// public class GetListAndAsListMapping : ScriptBase, IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) @@ -21,48 +17,44 @@ public class GetListAndAsListMapping : ScriptBase, IMapping { LogInformation("GetList and AsList Test - Starting"); - var instanceData = context.Instance?.Data; + var data = context.Instance.Data; - // Test GetList() - retrieve items from instance data by property name - var items = GetList(instanceData, "items"); - - // Test AsList() with a valid list - should return same items + var items = GetList(data, "items"); var asListFromValid = AsList(items); - - // Test AsList() with null - should return empty list (graceful degradation) var asListFromNull = AsList(null); - - // Test AsList() with a non-list value - should return empty list var asListFromString = AsList("not a list"); - LogInformation("GetList Test - Retrieved {0} items", args: new object[] { items.Count }); - - return Task.FromResult(new ScriptResponse - { - Key = "get-list-success", - Data = new - { - getListResult = new - { - success = true, - itemCount = items.Count, - getListWorked = items.Count == 3, - asListFromValidCount = asListFromValid.Count, - asListNullReturnsEmpty = asListFromNull.Count == 0, - asListInvalidReturnsEmpty = asListFromString.Count == 0 - } - }, - Tags = new[] { "collection-object-test", "get-list", "as-list", "success" } - }); + LogInformation($"GetList Test - Retrieved {items.Count} items"); + + dynamic result = new ExpandoObject(); + + if (HasProperty(data, "testId")) + result.testId = data.testId; + if (HasProperty(data, "startedAt")) + result.startedAt = data.startedAt; + if (HasProperty(data, "items")) + result.items = data.items; + if (HasProperty(data, "metadata")) + result.metadata = data.metadata; + if (HasProperty(data, "createAndSetResult")) + result.createAndSetResult = data.createAndSetResult; + + result.getListResult = new ExpandoObject(); + result.getListResult.success = true; + result.getListResult.itemCount = items.Count; + result.getListResult.getListWorked = items.Count == 3; + result.getListResult.asListFromValidCount = asListFromValid.Count; + result.getListResult.asListNullReturnsEmpty = asListFromNull.Count == 0; + result.getListResult.asListInvalidReturnsEmpty = asListFromString.Count == 0; + + return Task.FromResult(new ScriptResponse { Data = result }); } catch (Exception ex) { - LogError("GetList Test - Failed: {0}", args: new object[] { ex.Message }); - return Task.FromResult(new ScriptResponse - { - Key = "get-list-error", - Data = new { error = ex.Message } - }); + LogError($"GetList Test - Failed: {ex.Message}"); + dynamic errResult = new ExpandoObject(); + errResult.error = ex.Message; + return Task.FromResult(new ScriptResponse { Data = errResult }); } } } diff --git a/core/Workflows/collection-object-test/src/InitCollectionTestMapping.csx b/core/Workflows/collection-object-test/src/InitCollectionTestMapping.csx index 9221910..e3a4f2d 100644 --- a/core/Workflows/collection-object-test/src/InitCollectionTestMapping.csx +++ b/core/Workflows/collection-object-test/src/InitCollectionTestMapping.csx @@ -1,12 +1,9 @@ using System; +using System.Dynamic; using System.Threading.Tasks; using BBT.Workflow.Definitions; using BBT.Workflow.Scripting; -using BBT.Workflow.Scripting.Functions; -/// -/// Initial transition mapping - Initializes the collection/object test workflow -/// public class InitCollectionTestMapping : ScriptBase, IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) @@ -18,15 +15,13 @@ public class InitCollectionTestMapping : ScriptBase, IMapping { LogInformation("CollectionObjectTest - Workflow initialized"); + dynamic result = new ExpandoObject(); + result.testId = Guid.NewGuid().ToString(); + result.startedAt = DateTime.UtcNow.ToString("o"); + return Task.FromResult(new ScriptResponse { - Key = "init-success", - Data = new - { - testId = Guid.NewGuid().ToString(), - startedAt = DateTime.UtcNow - }, - Tags = new[] { "collection-object-test", "initialized" } + Data = result }); } } diff --git a/core/Workflows/collection-object-test/src/ListAddRemoveMapping.csx b/core/Workflows/collection-object-test/src/ListAddRemoveMapping.csx index 2134b40..b6fb71e 100644 --- a/core/Workflows/collection-object-test/src/ListAddRemoveMapping.csx +++ b/core/Workflows/collection-object-test/src/ListAddRemoveMapping.csx @@ -1,14 +1,9 @@ using System; +using System.Dynamic; using System.Threading.Tasks; using BBT.Workflow.Definitions; using BBT.Workflow.Scripting; -using BBT.Workflow.Scripting.Functions; -/// -/// Tests: ListAdd(), ListRemove() -/// Starts with Alice(active), Bob(inactive), Charlie(active). -/// Adds Diana(active) → removes all inactive (Bob) → ends with Alice, Charlie, Diana. -/// public class ListAddRemoveMapping : ScriptBase, IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) @@ -22,12 +17,11 @@ public class ListAddRemoveMapping : ScriptBase, IMapping { LogInformation("ListAdd and ListRemove Test - Starting"); - object? instanceData = context.Instance?.Data; - var items = GetList(instanceData, "items"); + var data = context.Instance.Data; + var items = GetList(data, "items"); var countBefore = ListCount(items); - // Test ListAdd() - add a new item dynamic newItem = CreateObject(); SetProperty(newItem, "id", "item-004"); SetProperty(newItem, "name", "Diana"); @@ -35,49 +29,53 @@ public class ListAddRemoveMapping : ScriptBase, IMapping SetProperty(newItem, "status", "active"); ListAdd(items, newItem); - var countAfterAdd = ListCount(items); - // Test ListRemove() - remove all inactive items (Bob) var removedCount = ListRemove(items, x => x.status == "inactive"); - var countAfterRemove = ListCount(items); - - // Verify no inactive items remain var hasInactiveAfterRemove = ListAny(items, x => x.status == "inactive"); - LogInformation("Add/Remove: before={0}, afterAdd={1}, removed={2}, afterRemove={3}", - args: new object[] { countBefore, countAfterAdd, removedCount, countAfterRemove }); + LogInformation($"Add/Remove: before={countBefore}, afterAdd={countAfterAdd}, removed={removedCount}, afterRemove={countAfterRemove}"); + + dynamic result = new ExpandoObject(); + + if (HasProperty(data, "testId")) + result.testId = data.testId; + if (HasProperty(data, "startedAt")) + result.startedAt = data.startedAt; + if (HasProperty(data, "metadata")) + result.metadata = data.metadata; + if (HasProperty(data, "createAndSetResult")) + result.createAndSetResult = data.createAndSetResult; + if (HasProperty(data, "getListResult")) + result.getListResult = data.getListResult; + if (HasProperty(data, "filterCountAnyResult")) + result.filterCountAnyResult = data.filterCountAnyResult; + if (HasProperty(data, "firstLastResult")) + result.firstLastResult = data.firstLastResult; + if (HasProperty(data, "listSelectResult")) + result.listSelectResult = data.listSelectResult; + + result.items = items; + + result.listAddRemoveResult = new ExpandoObject(); + result.listAddRemoveResult.success = true; + result.listAddRemoveResult.countBefore = countBefore; + result.listAddRemoveResult.countAfterAdd = countAfterAdd; + result.listAddRemoveResult.removedCount = removedCount; + result.listAddRemoveResult.countAfterRemove = countAfterRemove; + result.listAddRemoveResult.hasInactiveAfterRemove = hasInactiveAfterRemove; + result.listAddRemoveResult.addWorked = countAfterAdd == countBefore + 1; + result.listAddRemoveResult.removeWorked = removedCount == 1 && !hasInactiveAfterRemove; - return Task.FromResult(new ScriptResponse - { - Key = "add-remove-success", - Data = new - { - items = items, - listAddRemoveResult = new - { - success = true, - countBefore = countBefore, - countAfterAdd = countAfterAdd, - removedCount = removedCount, - countAfterRemove = countAfterRemove, - hasInactiveAfterRemove = hasInactiveAfterRemove, - addWorked = countAfterAdd == countBefore + 1, - removeWorked = removedCount == 1 && !hasInactiveAfterRemove - } - }, - Tags = new[] { "collection-object-test", "list-add", "list-remove", "success" } - }); + return Task.FromResult(new ScriptResponse { Data = result }); } catch (Exception ex) { - LogError("ListAdd/Remove Test - Failed: {0}", args: new object[] { ex.Message }); - return Task.FromResult(new ScriptResponse - { - Key = "add-remove-error", - Data = new { error = ex.Message } - }); + LogError($"ListAdd/Remove Test - Failed: {ex.Message}"); + dynamic errResult = new ExpandoObject(); + errResult.error = ex.Message; + return Task.FromResult(new ScriptResponse { Data = errResult }); } } } diff --git a/core/Workflows/collection-object-test/src/ListFilterCountAnyMapping.csx b/core/Workflows/collection-object-test/src/ListFilterCountAnyMapping.csx index 5751a27..2c163bd 100644 --- a/core/Workflows/collection-object-test/src/ListFilterCountAnyMapping.csx +++ b/core/Workflows/collection-object-test/src/ListFilterCountAnyMapping.csx @@ -1,13 +1,9 @@ using System; +using System.Dynamic; using System.Threading.Tasks; using BBT.Workflow.Definitions; using BBT.Workflow.Scripting; -using BBT.Workflow.Scripting.Functions; -/// -/// Tests: ListFilter(), ListCount(), ListAny() -/// Uses the items list (Alice=active, Bob=inactive, Charlie=active) from Instance.Data. -/// public class ListFilterCountAnyMapping : ScriptBase, IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) @@ -21,67 +17,61 @@ public class ListFilterCountAnyMapping : ScriptBase, IMapping { LogInformation("ListFilter, ListCount, ListAny Test - Starting"); - object instanceData = context.Instance?.Data; - var items = GetList(instanceData, "items"); + var data = context.Instance.Data; + var items = GetList(data, "items"); - // Test ListFilter() - filter active items (Alice + Charlie) var activeItems = ListFilter(items, x => x.status == "active"); - - // Test ListCount() - no predicate (total) var totalCount = ListCount(items); - - // Test ListCount() with predicate var activeCount = ListCount(items, x => x.status == "active"); var inactiveCount = ListCount(items, x => x.status == "inactive"); - - // Test ListAny() - no predicate (has any element) var hasItems = ListAny(items); - - // Test ListAny() with predicate var hasActive = ListAny(items, x => x.status == "active"); var hasAdminRole = ListAny(items, x => x.status == "admin"); - // Empty list checks var emptyList = CreateList(); var emptyHasItems = ListAny(emptyList); var emptyCount = ListCount(emptyList); - LogInformation("ListFilter: active={0}, inactive={1}, total={2}", - args: new object[] { activeCount, inactiveCount, totalCount }); + LogInformation($"ListFilter: active={activeCount}, inactive={inactiveCount}, total={totalCount}"); + + dynamic result = new ExpandoObject(); + + if (HasProperty(data, "testId")) + result.testId = data.testId; + if (HasProperty(data, "startedAt")) + result.startedAt = data.startedAt; + if (HasProperty(data, "items")) + result.items = data.items; + if (HasProperty(data, "metadata")) + result.metadata = data.metadata; + if (HasProperty(data, "createAndSetResult")) + result.createAndSetResult = data.createAndSetResult; + if (HasProperty(data, "getListResult")) + result.getListResult = data.getListResult; + + result.filterCountAnyResult = new ExpandoObject(); + result.filterCountAnyResult.success = true; + result.filterCountAnyResult.totalCount = totalCount; + result.filterCountAnyResult.activeCount = activeCount; + result.filterCountAnyResult.inactiveCount = inactiveCount; + result.filterCountAnyResult.activeItemsFiltered = activeItems.Count; + result.filterCountAnyResult.hasItems = hasItems; + result.filterCountAnyResult.hasActive = hasActive; + result.filterCountAnyResult.hasAdminRole = hasAdminRole; + result.filterCountAnyResult.emptyListHasItems = emptyHasItems; + result.filterCountAnyResult.emptyListCount = emptyCount; + result.filterCountAnyResult.filterWorked = activeItems.Count == 2; + result.filterCountAnyResult.countWorked = totalCount == 3 && activeCount == 2 && inactiveCount == 1; + result.filterCountAnyResult.anyWorked = hasItems && hasActive && !hasAdminRole && !emptyHasItems; - return Task.FromResult(new ScriptResponse - { - Key = "filter-count-any-success", - Data = new - { - filterCountAnyResult = new - { - success = true, - totalCount = totalCount, - activeCount = activeCount, - inactiveCount = inactiveCount, - activeItemsFiltered = activeItems.Count, - hasItems = hasItems, - hasActive = hasActive, - hasAdminRole = hasAdminRole, - emptyListHasItems = emptyHasItems, - emptyListCount = emptyCount, - filterWorked = activeItems.Count == 2, - countWorked = totalCount == 3 && activeCount == 2 && inactiveCount == 1, - anyWorked = hasItems && hasActive && !hasAdminRole && !emptyHasItems - } - }, - Tags = new[] { "collection-object-test", "list-filter", "list-count", "list-any", "success" } - }); + return Task.FromResult(new ScriptResponse { Data = result }); } catch (Exception ex) { - LogError("ListFilter/Count/Any Test - Failed: {0}", args: new object[] { ex.Message }); - return Task.FromResult(new ScriptResponse - { - Key = "filter-count-any-error", - Data = new { error = ex.Message } - }); + LogError($"ListFilter/Count/Any Test - Failed: {ex.Message}"); + dynamic errResult = new ExpandoObject(); + errResult.error = ex.Message; + return Task.FromResult(new ScriptResponse { Data = errResult }); } } } diff --git a/core/Workflows/collection-object-test/src/ListFirstLastMapping.csx b/core/Workflows/collection-object-test/src/ListFirstLastMapping.csx index 2add005..a8543b1 100644 --- a/core/Workflows/collection-object-test/src/ListFirstLastMapping.csx +++ b/core/Workflows/collection-object-test/src/ListFirstLastMapping.csx @@ -1,13 +1,9 @@ using System; +using System.Dynamic; using System.Threading.Tasks; using BBT.Workflow.Definitions; using BBT.Workflow.Scripting; -using BBT.Workflow.Scripting.Functions; -/// -/// Tests: ListFirst(), ListLast() -/// Items order: Alice(active), Bob(inactive), Charlie(active) -/// public class ListFirstLastMapping : ScriptBase, IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) @@ -21,64 +17,62 @@ public class ListFirstLastMapping : ScriptBase, IMapping { LogInformation("ListFirst and ListLast Test - Starting"); - object instanceData = context.Instance?.Data; - var items = GetList(instanceData, "items"); + var data = context.Instance.Data; + var items = GetList(data, "items"); - // Test ListFirst() without predicate → Alice var first = ListFirst(items); - - // Test ListFirst() with predicate → first active = Alice, first inactive = Bob var firstActive = ListFirst(items, x => x.status == "active"); var firstInactive = ListFirst(items, x => x.status == "inactive"); - - // Test ListFirst() with no match → null var firstNotFound = ListFirst(items, x => x.status == "admin"); - // Test ListLast() without predicate → Charlie var last = ListLast(items); - - // Test ListLast() with predicate → last active = Charlie var lastActive = ListLast(items, x => x.status == "active"); - // Test on empty list → null var emptyFirst = ListFirst(CreateList()); var emptyLast = ListLast(CreateList()); - LogInformation("First: {0}, Last: {1}", args: new object[] { first?.name, last?.name }); + LogInformation($"First: {first?.name}, Last: {last?.name}"); + + dynamic result = new ExpandoObject(); + + if (HasProperty(data, "testId")) + result.testId = data.testId; + if (HasProperty(data, "startedAt")) + result.startedAt = data.startedAt; + if (HasProperty(data, "items")) + result.items = data.items; + if (HasProperty(data, "metadata")) + result.metadata = data.metadata; + if (HasProperty(data, "createAndSetResult")) + result.createAndSetResult = data.createAndSetResult; + if (HasProperty(data, "getListResult")) + result.getListResult = data.getListResult; + if (HasProperty(data, "filterCountAnyResult")) + result.filterCountAnyResult = data.filterCountAnyResult; + + result.firstLastResult = new ExpandoObject(); + result.firstLastResult.success = true; + result.firstLastResult.firstName = (string)(first?.name ?? "null"); + result.firstLastResult.lastName = (string)(last?.name ?? "null"); + result.firstLastResult.firstActiveName = (string)(firstActive?.name ?? "null"); + result.firstLastResult.firstInactiveName = (string)(firstInactive?.name ?? "null"); + result.firstLastResult.lastActiveName = (string)(lastActive?.name ?? "null"); + result.firstLastResult.firstNotFoundIsNull = firstNotFound == null; + result.firstLastResult.emptyFirstIsNull = emptyFirst == null; + result.firstLastResult.emptyLastIsNull = emptyLast == null; + result.firstLastResult.firstWorked = first?.id == "item-001"; + result.firstLastResult.lastWorked = last?.id == "item-003"; + result.firstLastResult.firstWithPredicateWorked = firstActive?.id == "item-001"; + result.firstLastResult.lastWithPredicateWorked = lastActive?.id == "item-003"; - return Task.FromResult(new ScriptResponse - { - Key = "first-last-success", - Data = new - { - firstLastResult = new - { - success = true, - firstName = (string)(first?.name ?? "null"), - lastName = (string)(last?.name ?? "null"), - firstActiveName = (string)(firstActive?.name ?? "null"), - firstInactiveName = (string)(firstInactive?.name ?? "null"), - lastActiveName = (string)(lastActive?.name ?? "null"), - firstNotFoundIsNull = firstNotFound == null, - emptyFirstIsNull = emptyFirst == null, - emptyLastIsNull = emptyLast == null, - firstWorked = first?.id == "item-001", - lastWorked = last?.id == "item-003", - firstWithPredicateWorked = firstActive?.id == "item-001", - lastWithPredicateWorked = lastActive?.id == "item-003" - } - }, - Tags = new[] { "collection-object-test", "list-first", "list-last", "success" } - }); + return Task.FromResult(new ScriptResponse { Data = result }); } catch (Exception ex) { - LogError("ListFirst/Last Test - Failed: {0}", args: new object[] { ex.Message }); - return Task.FromResult(new ScriptResponse - { - Key = "first-last-error", - Data = new { error = ex.Message } - }); + LogError($"ListFirst/Last Test - Failed: {ex.Message}"); + dynamic errResult = new ExpandoObject(); + errResult.error = ex.Message; + return Task.FromResult(new ScriptResponse { Data = errResult }); } } } diff --git a/core/Workflows/collection-object-test/src/ListSelectMapping.csx b/core/Workflows/collection-object-test/src/ListSelectMapping.csx index 7dfca71..a473d05 100644 --- a/core/Workflows/collection-object-test/src/ListSelectMapping.csx +++ b/core/Workflows/collection-object-test/src/ListSelectMapping.csx @@ -1,13 +1,9 @@ using System; +using System.Dynamic; using System.Threading.Tasks; using BBT.Workflow.Definitions; using BBT.Workflow.Scripting; -using BBT.Workflow.Scripting.Functions; -/// -/// Tests: ListSelect<TResult>() -/// Projects list items into different types: string, int, object -/// public class ListSelectMapping : ScriptBase, IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) @@ -21,54 +17,56 @@ public class ListSelectMapping : ScriptBase, IMapping { LogInformation("ListSelect Test - Starting"); - object instanceData = context.Instance?.Data; - var items = GetList(instanceData, "items"); + var data = context.Instance.Data; + var items = GetList(data, "items"); - // Test ListSelect() - extract names var names = ListSelect(items, x => (string)x.name); - - // Test ListSelect() - extract ages var ages = ListSelect(items, x => (int)x.age); - - // Test ListSelect() with transformation var labels = ListSelect(items, x => $"{x.name} ({x.status})"); - - // Test ListSelect on empty list - should return empty list var emptyResult = ListSelect(CreateList(), x => (string)x.name); - LogInformation("ListSelect: {0} names, {1} ages", args: new object[] { names.Count, ages.Count }); + LogInformation($"ListSelect: {names.Count} names, {ages.Count} ages"); + + dynamic result = new ExpandoObject(); + + if (HasProperty(data, "testId")) + result.testId = data.testId; + if (HasProperty(data, "startedAt")) + result.startedAt = data.startedAt; + if (HasProperty(data, "items")) + result.items = data.items; + if (HasProperty(data, "metadata")) + result.metadata = data.metadata; + if (HasProperty(data, "createAndSetResult")) + result.createAndSetResult = data.createAndSetResult; + if (HasProperty(data, "getListResult")) + result.getListResult = data.getListResult; + if (HasProperty(data, "filterCountAnyResult")) + result.filterCountAnyResult = data.filterCountAnyResult; + if (HasProperty(data, "firstLastResult")) + result.firstLastResult = data.firstLastResult; + + result.listSelectResult = new ExpandoObject(); + result.listSelectResult.success = true; + result.listSelectResult.names = names; + result.listSelectResult.ages = ages; + result.listSelectResult.labels = labels; + result.listSelectResult.namesCount = names.Count; + result.listSelectResult.agesCount = ages.Count; + result.listSelectResult.emptySelectCount = emptyResult.Count; + result.listSelectResult.selectStringWorked = names.Count == 3 && names[0] == "Alice"; + result.listSelectResult.selectIntWorked = ages.Count == 3 && ages[0] == 30; + result.listSelectResult.selectTransformWorked = labels.Count == 3; + result.listSelectResult.emptySelectWorked = emptyResult.Count == 0; - return Task.FromResult(new ScriptResponse - { - Key = "list-select-success", - Data = new - { - listSelectResult = new - { - success = true, - names = names, - ages = ages, - labels = labels, - namesCount = names.Count, - agesCount = ages.Count, - emptySelectCount = emptyResult.Count, - selectStringWorked = names.Count == 3 && names[0] == "Alice", - selectIntWorked = ages.Count == 3 && ages[0] == 30, - selectTransformWorked = labels.Count == 3, - emptySelectWorked = emptyResult.Count == 0 - } - }, - Tags = new[] { "collection-object-test", "list-select", "success" } - }); + return Task.FromResult(new ScriptResponse { Data = result }); } catch (Exception ex) { - LogError("ListSelect Test - Failed: {0}", args: new object[] { ex.Message }); - return Task.FromResult(new ScriptResponse - { - Key = "list-select-error", - Data = new { error = ex.Message } - }); + LogError($"ListSelect Test - Failed: {ex.Message}"); + dynamic errResult = new ExpandoObject(); + errResult.error = ex.Message; + return Task.FromResult(new ScriptResponse { Data = errResult }); } } } diff --git a/core/Workflows/collection-object-test/src/RemovePropertyToDictionaryMapping.csx b/core/Workflows/collection-object-test/src/RemovePropertyToDictionaryMapping.csx index 68bec03..568b3e9 100644 --- a/core/Workflows/collection-object-test/src/RemovePropertyToDictionaryMapping.csx +++ b/core/Workflows/collection-object-test/src/RemovePropertyToDictionaryMapping.csx @@ -1,13 +1,9 @@ using System; +using System.Dynamic; using System.Threading.Tasks; using BBT.Workflow.Definitions; using BBT.Workflow.Scripting; -using BBT.Workflow.Scripting.Functions; -/// -/// Tests: RemoveProperty(), ToDictionary(), HasProperty() -/// Creates a dynamic object, removes a property, converts to dictionary. -/// public class RemovePropertyToDictionaryMapping : ScriptBase, IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) @@ -21,74 +17,81 @@ public class RemovePropertyToDictionaryMapping : ScriptBase, IMapping { LogInformation("RemoveProperty and ToDictionary Test - Starting"); - // Create a test object with multiple properties dynamic testObj = CreateObject(); SetProperty(testObj, "id", "test-001"); SetProperty(testObj, "name", "Test Object"); SetProperty(testObj, "tempField", "this will be removed"); SetProperty(testObj, "keepField", "this stays"); - // Verify property exists before removal (uses HasProperty from ScriptBase) var hadTempField = HasProperty(testObj, "tempField"); var hadKeepField = HasProperty(testObj, "keepField"); - // Test RemoveProperty() var removeResult = RemoveProperty(testObj, "tempField"); - // Verify property is gone var hasTempFieldAfter = HasProperty(testObj, "tempField"); var hasKeepFieldAfter = HasProperty(testObj, "keepField"); - // Test RemoveProperty() on non-existent property → false var removeNonExistent = RemoveProperty(testObj, "doesNotExist"); - // Test ToDictionary() var dict = ToDictionary(testObj); var dictHasId = dict.ContainsKey("id"); var dictHasName = dict.ContainsKey("name"); var dictHasTemp = dict.ContainsKey("tempField"); var dictHasKeep = dict.ContainsKey("keepField"); - // Test ToDictionary() with null → empty dictionary var emptyDict = ToDictionary(null); - LogInformation("RemoveProperty: removed={0}, dictCount={1}", args: new object[] { removeResult, dict.Count }); + LogInformation($"RemoveProperty: removed={removeResult}, dictCount={dict.Count}"); - return Task.FromResult(new ScriptResponse - { - Key = "remove-to-dict-success", - Data = new - { - removeToDictResult = new - { - success = true, - hadTempFieldBefore = hadTempField, - hadKeepFieldBefore = hadKeepField, - removePropertyResult = removeResult, - hasTempFieldAfterRemove = hasTempFieldAfter, - hasKeepFieldAfterRemove = hasKeepFieldAfter, - removeNonExistentReturnsFalse = !removeNonExistent, - dictCount = dict.Count, - dictHasId = dictHasId, - dictHasName = dictHasName, - dictRemovedTempField = !dictHasTemp, - dictKeptKeepField = dictHasKeep, - nullToDictReturnsEmpty = emptyDict.Count == 0, - removePropertyWorked = removeResult && !hasTempFieldAfter && hasKeepFieldAfter, - toDictionaryWorked = dictHasId && dictHasName && !dictHasTemp && dictHasKeep - } - }, - Tags = new[] { "collection-object-test", "remove-property", "to-dictionary", "has-property", "success" } - }); + var data = context.Instance.Data; + dynamic result = new ExpandoObject(); + + if (HasProperty(data, "testId")) + result.testId = data.testId; + if (HasProperty(data, "startedAt")) + result.startedAt = data.startedAt; + if (HasProperty(data, "items")) + result.items = data.items; + if (HasProperty(data, "metadata")) + result.metadata = data.metadata; + if (HasProperty(data, "createAndSetResult")) + result.createAndSetResult = data.createAndSetResult; + if (HasProperty(data, "getListResult")) + result.getListResult = data.getListResult; + if (HasProperty(data, "filterCountAnyResult")) + result.filterCountAnyResult = data.filterCountAnyResult; + if (HasProperty(data, "firstLastResult")) + result.firstLastResult = data.firstLastResult; + if (HasProperty(data, "listSelectResult")) + result.listSelectResult = data.listSelectResult; + if (HasProperty(data, "listAddRemoveResult")) + result.listAddRemoveResult = data.listAddRemoveResult; + + result.removeToDictResult = new ExpandoObject(); + result.removeToDictResult.success = true; + result.removeToDictResult.hadTempFieldBefore = hadTempField; + result.removeToDictResult.hadKeepFieldBefore = hadKeepField; + result.removeToDictResult.removePropertyResult = removeResult; + result.removeToDictResult.hasTempFieldAfterRemove = hasTempFieldAfter; + result.removeToDictResult.hasKeepFieldAfterRemove = hasKeepFieldAfter; + result.removeToDictResult.removeNonExistentReturnsFalse = !removeNonExistent; + result.removeToDictResult.dictCount = dict.Count; + result.removeToDictResult.dictHasId = dictHasId; + result.removeToDictResult.dictHasName = dictHasName; + result.removeToDictResult.dictRemovedTempField = !dictHasTemp; + result.removeToDictResult.dictKeptKeepField = dictHasKeep; + result.removeToDictResult.nullToDictReturnsEmpty = emptyDict.Count == 0; + result.removeToDictResult.removePropertyWorked = removeResult && !hasTempFieldAfter && hasKeepFieldAfter; + result.removeToDictResult.toDictionaryWorked = dictHasId && dictHasName && !dictHasTemp && dictHasKeep; + + return Task.FromResult(new ScriptResponse { Data = result }); } catch (Exception ex) { - LogError("RemoveProperty/ToDictionary Test - Failed: {0}", args: new object[] { ex.Message }); - return Task.FromResult(new ScriptResponse - { - Key = "remove-to-dict-error", - Data = new { error = ex.Message } - }); + LogError($"RemoveProperty/ToDictionary Test - Failed: {ex.Message}"); + dynamic errResult = new ExpandoObject(); + errResult.error = ex.Message; + return Task.FromResult(new ScriptResponse { Data = errResult }); } } } diff --git a/doc/integration-test-documentation.md b/docs/integration-test-documentation.md similarity index 98% rename from doc/integration-test-documentation.md rename to docs/integration-test-documentation.md index 83c9c30..a4f7055 100644 --- a/doc/integration-test-documentation.md +++ b/docs/integration-test-documentation.md @@ -1046,11 +1046,11 @@ collection-object-test-workflow (F) │ ├── [startTransition] start-collection-test │ └── onExecutionTasks: -│ └── script-task + InitCollectionTestMapping.csx +│ └── collection-object-script-task + InitCollectionTestMapping.csx │ ├── test-create-and-set-state (Initial, stateType:1) │ ├── onEntries: -│ │ └── script-task + CreateAndSetMapping.csx +│ │ └── collection-object-script-task + CreateAndSetMapping.csx │ │ (CreateObject, CreateList, SetProperty, ListAdd) │ └── Transitions: │ └── create-and-set-completed (Auto) → test-get-list-state @@ -1058,7 +1058,7 @@ collection-object-test-workflow (F) │ ├── test-get-list-state (Intermediate, stateType:2) │ ├── onEntries: -│ │ └── script-task + GetListAndAsListMapping.csx +│ │ └── collection-object-script-task + GetListAndAsListMapping.csx │ │ (GetList, AsList — null/non-list edge case) │ └── Transitions: │ └── get-list-completed (Auto) → test-filter-count-any-state @@ -1066,7 +1066,7 @@ collection-object-test-workflow (F) │ ├── test-filter-count-any-state (Intermediate, stateType:2) │ ├── onEntries: -│ │ └── script-task + ListFilterCountAnyMapping.csx +│ │ └── collection-object-script-task + ListFilterCountAnyMapping.csx │ │ (ListFilter, ListCount, ListAny — predicate'li/predicate'siz, empty list) │ └── Transitions: │ └── filter-count-any-completed (Auto) → test-first-last-state @@ -1074,7 +1074,7 @@ collection-object-test-workflow (F) │ ├── test-first-last-state (Intermediate, stateType:2) │ ├── onEntries: -│ │ └── script-task + ListFirstLastMapping.csx +│ │ └── collection-object-script-task + ListFirstLastMapping.csx │ │ (ListFirst, ListLast — predicate, no-match → null, empty list) │ └── Transitions: │ └── first-last-completed (Auto) → test-list-select-state @@ -1082,7 +1082,7 @@ collection-object-test-workflow (F) │ ├── test-list-select-state (Intermediate, stateType:2) │ ├── onEntries: -│ │ └── script-task + ListSelectMapping.csx +│ │ └── collection-object-script-task + ListSelectMapping.csx │ │ (ListSelect, ListSelect, transformation, empty list) │ └── Transitions: │ └── list-select-completed (Auto) → test-add-remove-state @@ -1090,7 +1090,7 @@ collection-object-test-workflow (F) │ ├── test-add-remove-state (Intermediate, stateType:2) │ ├── onEntries: -│ │ └── script-task + ListAddRemoveMapping.csx +│ │ └── collection-object-script-task + ListAddRemoveMapping.csx │ │ (ListAdd, ListRemove — predicate ile sayim, count dogrulama) │ └── Transitions: │ └── add-remove-completed (Auto) → test-remove-prop-to-dict-state @@ -1098,7 +1098,7 @@ collection-object-test-workflow (F) │ ├── test-remove-prop-to-dict-state (Intermediate, stateType:2) │ ├── onEntries: -│ │ └── script-task + RemovePropertyToDictionaryMapping.csx +│ │ └── collection-object-script-task + RemovePropertyToDictionaryMapping.csx │ │ (RemoveProperty, ToDictionary, HasProperty — null edge case) │ └── Transitions: │ └── remove-prop-to-dict-completed (Auto) → test-completed-state @@ -1133,7 +1133,7 @@ collection-object-test-workflow (F) | Tip | Anahtar | Dosya | |-----|---------|-------| -| Task | `script-task` (paylasimli, type:7) | Tasks/lifecycle-transitions/script-task.json | +| Task | `collection-object-script-task` (type:7) | Tasks/collection-object-test/collection-object-script-task.json | | CSX | InitCollectionTestMapping | Workflows/collection-object-test/src/InitCollectionTestMapping.csx | | CSX | CreateAndSetMapping | Workflows/collection-object-test/src/CreateAndSetMapping.csx | | CSX | GetListAndAsListMapping | Workflows/collection-object-test/src/GetListAndAsListMapping.csx | @@ -1144,7 +1144,8 @@ collection-object-test-workflow (F) | CSX | RemovePropertyToDictionaryMapping | Workflows/collection-object-test/src/RemovePropertyToDictionaryMapping.csx | | CSX | AlwaysTrueRule | Workflows/collection-object-test/src/AlwaysTrueRule.csx | -**HTTP / C# integration test:** Henuz eklenmedi. +**HTTP test:** [`api-tests/collection-object-test/collection-object-test-workflow.http`](../api-tests/collection-object-test/collection-object-test-workflow.http) — start, `functions/state`, `functions/data`. +**C# integration test:** [`tests/Core/Tests/collection-object-test/CollectionObjectTestWorkflowTests.cs`](../tests/Core/Tests/collection-object-test/CollectionObjectTestWorkflowTests.cs). --- diff --git a/tests/Core/Tests/collection-object-test/CollectionObjectInstanceDataAssertions.cs b/tests/Core/Tests/collection-object-test/CollectionObjectInstanceDataAssertions.cs new file mode 100644 index 0000000..13f3986 --- /dev/null +++ b/tests/Core/Tests/collection-object-test/CollectionObjectInstanceDataAssertions.cs @@ -0,0 +1,187 @@ +using System.Text.Json; +using Core.IntegrationTests.Helpers; +using Xunit; + +namespace Core.IntegrationTests.Tests.CollectionObjectTest; + +/// +/// Assertions for collection-object-test-workflow instance attributes after the full +/// ScriptBase collection/object API pipeline completes. +/// +public static class CollectionObjectInstanceDataAssertions +{ + public static void AssertInitialization(JsonElement attrs) + { + JsonElementAssertions.AssertPropertyNonEmptyString( + attrs, + "testId", + "InitCollectionTestMapping should write testId." + ); + } + + public static void AssertCreateAndSet(JsonElement attrs) + { + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["createAndSetResult"], + "success" + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["createAndSetResult"], + "propertiesSet" + ); + } + + public static void AssertGetListAndAsList(JsonElement attrs) + { + JsonElementAssertions.AssertNestedPropertyTrue(attrs, ["getListResult"], "success"); + JsonElementAssertions.AssertNestedPropertyTrue(attrs, ["getListResult"], "getListWorked"); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["getListResult"], + "asListNullReturnsEmpty" + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["getListResult"], + "asListInvalidReturnsEmpty" + ); + } + + public static void AssertFilterCountAny(JsonElement attrs) + { + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["filterCountAnyResult"], + "success" + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["filterCountAnyResult"], + "filterWorked" + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["filterCountAnyResult"], + "countWorked" + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["filterCountAnyResult"], + "anyWorked" + ); + } + + public static void AssertFirstLast(JsonElement attrs) + { + JsonElementAssertions.AssertNestedPropertyTrue(attrs, ["firstLastResult"], "success"); + JsonElementAssertions.AssertNestedPropertyTrue(attrs, ["firstLastResult"], "firstWorked"); + JsonElementAssertions.AssertNestedPropertyTrue(attrs, ["firstLastResult"], "lastWorked"); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["firstLastResult"], + "firstWithPredicateWorked" + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["firstLastResult"], + "lastWithPredicateWorked" + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["firstLastResult"], + "firstNotFoundIsNull" + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["firstLastResult"], + "emptyFirstIsNull" + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["firstLastResult"], + "emptyLastIsNull" + ); + } + + public static void AssertListSelect(JsonElement attrs) + { + JsonElementAssertions.AssertNestedPropertyTrue(attrs, ["listSelectResult"], "success"); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["listSelectResult"], + "selectStringWorked" + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["listSelectResult"], + "selectIntWorked" + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["listSelectResult"], + "selectTransformWorked" + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["listSelectResult"], + "emptySelectWorked" + ); + } + + public static void AssertListAddRemove(JsonElement attrs) + { + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["listAddRemoveResult"], + "success" + ); + JsonElementAssertions.AssertNestedPropertyTrue(attrs, ["listAddRemoveResult"], "addWorked"); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["listAddRemoveResult"], + "removeWorked" + ); + } + + public static void AssertRemovePropertyToDictionary(JsonElement attrs) + { + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["removeToDictResult"], + "success" + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["removeToDictResult"], + "removePropertyWorked" + ); + JsonElementAssertions.AssertNestedPropertyTrue( + attrs, + ["removeToDictResult"], + "toDictionaryWorked" + ); + } + + /// Runs all assertions in pipeline order (matches workflow state order). + public static void AssertFullHappyPathAttributes(JsonElement attrs) + { + Assert.False( + attrs.ValueKind == JsonValueKind.Object + && attrs.TryGetProperty("error", out var rootErr) + && rootErr.ValueKind == JsonValueKind.String + && !string.IsNullOrWhiteSpace(rootErr.GetString()), + "attributes should not contain a top-level error string after happy path." + ); + + AssertInitialization(attrs); + AssertCreateAndSet(attrs); + AssertGetListAndAsList(attrs); + AssertFilterCountAny(attrs); + AssertFirstLast(attrs); + AssertListSelect(attrs); + AssertListAddRemove(attrs); + AssertRemovePropertyToDictionary(attrs); + } +} diff --git a/tests/Core/Tests/collection-object-test/CollectionObjectTestWorkflowTests.cs b/tests/Core/Tests/collection-object-test/CollectionObjectTestWorkflowTests.cs new file mode 100644 index 0000000..a9da17e --- /dev/null +++ b/tests/Core/Tests/collection-object-test/CollectionObjectTestWorkflowTests.cs @@ -0,0 +1,42 @@ +using Core.IntegrationTests.Helpers; +using Core.IntegrationTests.Infrastructure; +using Xunit; + +namespace Core.IntegrationTests.Tests.CollectionObjectTest; + +/// +/// Integration tests for collection-object-test-workflow: linear pipeline of ScriptBase +/// collection/object API mappings with auto transitions to test-completed-state. +/// +public class CollectionObjectTestWorkflowTests : IntegrationTestBase +{ + private const string WorkflowKey = "collection-object-test-workflow"; + private static readonly TimeSpan StateTimeout = TimeSpan.FromSeconds(60); + private readonly WorkflowInstanceTestHelper _wf; + + public CollectionObjectTestWorkflowTests(VNextTestEnvironment environment) + : base(environment) + { + _wf = new WorkflowInstanceTestHelper(Api, WorkflowKey); + } + + [Fact] + public async Task HappyPath_AllScriptBaseApis_CompleteSuccessfully() + { + var body = new + { + key = WorkflowInstanceTestHelper.UniqueInstanceKey("collection-object-test"), + tags = new[] { "integration-test", "collection-object-test" }, + attributes = new { }, + }; + + var instanceId = await _wf.StartInstanceIdAsync(body); + await _wf.WaitForStateAsync(instanceId, "test-completed-state", StateTimeout); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("C", StateFunctionJson.ExtractStatus(stateBody)); + + var attrs = await _wf.GetAttributesAsync(instanceId); + CollectionObjectInstanceDataAssertions.AssertFullHappyPathAttributes(attrs); + } +} From a110d5f0e1faf2990abc35bc7cc200936f0f99b4 Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Tue, 5 May 2026 09:44:10 +0300 Subject: [PATCH 16/24] collection-object-test has passed --- .../postman-collection-object-test.json | 219 ++++++++++++++++++ core.link.json | 2 +- .../collection-object-test-workflow.json | 50 +++- .../src/ListAddRemoveMapping.csx | 8 +- .../src/ListFilterCountAnyMapping.csx | 23 +- .../src/ListFirstLastMapping.csx | 39 ++-- .../src/ListSelectMapping.csx | 11 +- .../CollectionObjectTestWorkflowTests.cs | 1 + 8 files changed, 317 insertions(+), 36 deletions(-) create mode 100644 api-tests/collection-object-test/postman-collection-object-test.json diff --git a/api-tests/collection-object-test/postman-collection-object-test.json b/api-tests/collection-object-test/postman-collection-object-test.json new file mode 100644 index 0000000..69de6cf --- /dev/null +++ b/api-tests/collection-object-test/postman-collection-object-test.json @@ -0,0 +1,219 @@ +{ + "info": { + "_postman_id": "c8a41e2b-9f03-4c5d-a1d6-2b9e7f3d1a44", + "name": "vNext Test - Collection Object (Grup 10)", + "description": "collection-object-test-workflow — ScriptBase dinamik koleksiyon/nesne API pipeline'i.\n\nAkış: start (InitCollectionTestMapping) → test-create-and-set-state → … → otomatik zincir (AlwaysTrueRule) → test-completed-state.\n\n- Adım 1: `sync=true` ile başlatma (ara otomatik geçişler tamamlanana kadar bekler; `.http` örneği sync kullanmıyorsa sorgudan kaldırıp state için yeniden GET yapın).\n- Beklenen son: state `test-completed-state`, status `C`.\n- `functions/data` içinde: `testId`, `createAndSetResult`, `getListResult`, … `removeToDictResult`.\n\nÖnkoşul: workflow + task publish (`core/collection-object-test-workflow`).", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "variable": [ + { + "key": "baseUrl", + "value": "http://localhost:4201", + "type": "string" + }, + { + "key": "apiVersion", + "value": "1", + "type": "string" + }, + { + "key": "domain", + "value": "core", + "type": "string" + }, + { + "key": "workflow", + "value": "collection-object-test-workflow", + "type": "string" + }, + { + "key": "collectionObjectInstanceId", + "value": "", + "type": "string" + } + ], + "item": [ + { + "name": "collection-object-test-workflow", + "item": [ + { + "name": "Adim 1: Workflow baslat (sync)", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": ["{{baseUrl}}"], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"collection-object-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"collection-object-test\"],\n \"attributes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('HTTP 2xx', function () { pm.response.to.be.success; });", + "var jsonData = pm.response.json();", + "if (jsonData && jsonData.id) {", + " pm.collectionVariables.set('collectionObjectInstanceId', jsonData.id);", + " console.log('collectionObjectInstanceId:', jsonData.id);", + "} else {", + " console.warn('Yant id icermiyor', jsonData);", + "}", + "pm.test('id donmeli', function () {", + " pm.expect(jsonData).to.have.property('id');", + "});" + ] + } + } + ] + }, + { + "name": "Adim 2: GET functions/state (test-completed-state + C)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{collectionObjectInstanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{collectionObjectInstanceId}}", + "functions", + "state" + ] + } + }, + "response": [], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "if (!pm.collectionVariables.get('collectionObjectInstanceId')) {", + " throw new Error('Once Adim 1 calistirin (collectionObjectInstanceId bos).');", + "}" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('HTTP 2xx', function () { pm.response.to.be.success; });", + "var b = pm.response.json();", + "var state = (b && (b.state || (b.data && b.data.state))) || '';", + "var status = (b && (b.status || (b.data && b.data.status))) || '';", + "pm.test('state test-completed-state', function () {", + " pm.expect(String(state)).to.eql('test-completed-state');", + "});", + "pm.test('status Completed (C)', function () {", + " pm.expect(String(status)).to.eql('C');", + "});" + ] + } + } + ] + }, + { + "name": "Adim 3: GET functions/data (pipeline bayraklari)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{collectionObjectInstanceId}}/functions/data", + "host": ["{{baseUrl}}"], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{collectionObjectInstanceId}}", + "functions", + "data" + ] + } + }, + "response": [], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "if (!pm.collectionVariables.get('collectionObjectInstanceId')) {", + " throw new Error('Once Adim 1 calistirin (collectionObjectInstanceId bos).');", + "}" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('HTTP 2xx', function () { pm.response.to.be.success; });", + "var body = pm.response.json();", + "var attrs = (body && body.attributes) ? body.attributes : body;", + "if (body && body.data && body.data.attributes) attrs = body.data.attributes;", + "function has(obj, path) {", + " var cur = obj;", + " for (var i = 0; i < path.length; i++) {", + " if (!cur || typeof cur !== 'object' || !(path[i] in cur)) return false;", + " cur = cur[path[i]];", + " }", + " return true;", + "}", + "pm.test('testId var', function () { pm.expect(has(attrs, ['testId'])).to.be.true; });", + "pm.test('createAndSetResult.success', function () { pm.expect(has(attrs, ['createAndSetResult', 'success'])).to.be.true; });", + "pm.test('getListResult.success', function () { pm.expect(has(attrs, ['getListResult', 'success'])).to.be.true; });", + "pm.test('removeToDictResult.success', function () { pm.expect(has(attrs, ['removeToDictResult', 'success'])).to.be.true; });" + ] + } + } + ] + } + ] + } + ] +} diff --git a/core.link.json b/core.link.json index 9af3134..18bea35 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-05-04T14:00:05.701Z" + "importedAt": "2026-05-05T06:32:51.056Z" } \ No newline at end of file diff --git a/core/Workflows/collection-object-test/collection-object-test-workflow.json b/core/Workflows/collection-object-test/collection-object-test-workflow.json index b3a5263..84b0531 100644 --- a/core/Workflows/collection-object-test/collection-object-test-workflow.json +++ b/core/Workflows/collection-object-test/collection-object-test-workflow.json @@ -41,7 +41,7 @@ "sharedTransitions": [], "startTransition": { "key": "start-collection-test", - "target": "test-create-and-set-state", + "target": "test-init-state", "triggerType": 0, "versionStrategy": "Minor", "labels": [ @@ -68,10 +68,48 @@ }, "states": [ { - "key": "test-create-and-set-state", + "key": "test-init-state", "stateType": 1, "subType": 0, "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Initial passthrough state" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "init-to-create-and-set", + "target": "test-create-and-set-state", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Proceed to CreateAndSet" + } + ], + "schema": null, + "rule": { + "location": "./src/AlwaysTrueRule.csx", + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBhc3luYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiB0cnVlOwogICAgfQp9Cgo=" + }, + "timer": null, + "view": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "test-create-and-set-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", "labels": [ { "language": "en-US", @@ -195,7 +233,7 @@ }, "mapping": { "location": "./src/ListFilterCountAnyMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIExpc3RGaWx0ZXJDb3VudEFueU1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIkxpc3RGaWx0ZXIsIExpc3RDb3VudCwgTGlzdEFueSBUZXN0IC0gU3RhcnRpbmciKTsKCiAgICAgICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgICAgICB2YXIgaXRlbXMgPSBHZXRMaXN0KGRhdGEsICJpdGVtcyIpOwoKICAgICAgICAgICAgdmFyIGFjdGl2ZUl0ZW1zID0gTGlzdEZpbHRlcihpdGVtcywgeCA9PiB4LnN0YXR1cyA9PSAiYWN0aXZlIik7CiAgICAgICAgICAgIHZhciB0b3RhbENvdW50ID0gTGlzdENvdW50KGl0ZW1zKTsKICAgICAgICAgICAgdmFyIGFjdGl2ZUNvdW50ID0gTGlzdENvdW50KGl0ZW1zLCB4ID0+IHguc3RhdHVzID09ICJhY3RpdmUiKTsKICAgICAgICAgICAgdmFyIGluYWN0aXZlQ291bnQgPSBMaXN0Q291bnQoaXRlbXMsIHggPT4geC5zdGF0dXMgPT0gImluYWN0aXZlIik7CiAgICAgICAgICAgIHZhciBoYXNJdGVtcyA9IExpc3RBbnkoaXRlbXMpOwogICAgICAgICAgICB2YXIgaGFzQWN0aXZlID0gTGlzdEFueShpdGVtcywgeCA9PiB4LnN0YXR1cyA9PSAiYWN0aXZlIik7CiAgICAgICAgICAgIHZhciBoYXNBZG1pblJvbGUgPSBMaXN0QW55KGl0ZW1zLCB4ID0+IHguc3RhdHVzID09ICJhZG1pbiIpOwoKICAgICAgICAgICAgdmFyIGVtcHR5TGlzdCA9IENyZWF0ZUxpc3QoKTsKICAgICAgICAgICAgdmFyIGVtcHR5SGFzSXRlbXMgPSBMaXN0QW55KGVtcHR5TGlzdCk7CiAgICAgICAgICAgIHZhciBlbXB0eUNvdW50ID0gTGlzdENvdW50KGVtcHR5TGlzdCk7CgogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkxpc3RGaWx0ZXI6IGFjdGl2ZT17YWN0aXZlQ291bnR9LCBpbmFjdGl2ZT17aW5hY3RpdmVDb3VudH0sIHRvdGFsPXt0b3RhbENvdW50fSIpOwoKICAgICAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkQXQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpdGVtcyIpKQogICAgICAgICAgICAgICAgcmVzdWx0Lml0ZW1zID0gZGF0YS5pdGVtczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJtZXRhZGF0YSIpKQogICAgICAgICAgICAgICAgcmVzdWx0Lm1ldGFkYXRhID0gZGF0YS5tZXRhZGF0YTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcmVhdGVBbmRTZXRSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5jcmVhdGVBbmRTZXRSZXN1bHQgPSBkYXRhLmNyZWF0ZUFuZFNldFJlc3VsdDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJnZXRMaXN0UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuZ2V0TGlzdFJlc3VsdCA9IGRhdGEuZ2V0TGlzdFJlc3VsdDsKCiAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdC5zdWNjZXNzID0gdHJ1ZTsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0LnRvdGFsQ291bnQgPSB0b3RhbENvdW50OwogICAgICAgICAgICByZXN1bHQuZmlsdGVyQ291bnRBbnlSZXN1bHQuYWN0aXZlQ291bnQgPSBhY3RpdmVDb3VudDsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0LmluYWN0aXZlQ291bnQgPSBpbmFjdGl2ZUNvdW50OwogICAgICAgICAgICByZXN1bHQuZmlsdGVyQ291bnRBbnlSZXN1bHQuYWN0aXZlSXRlbXNGaWx0ZXJlZCA9IGFjdGl2ZUl0ZW1zLkNvdW50OwogICAgICAgICAgICByZXN1bHQuZmlsdGVyQ291bnRBbnlSZXN1bHQuaGFzSXRlbXMgPSBoYXNJdGVtczsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0Lmhhc0FjdGl2ZSA9IGhhc0FjdGl2ZTsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0Lmhhc0FkbWluUm9sZSA9IGhhc0FkbWluUm9sZTsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0LmVtcHR5TGlzdEhhc0l0ZW1zID0gZW1wdHlIYXNJdGVtczsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0LmVtcHR5TGlzdENvdW50ID0gZW1wdHlDb3VudDsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0LmZpbHRlcldvcmtlZCA9IGFjdGl2ZUl0ZW1zLkNvdW50ID09IDI7CiAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdC5jb3VudFdvcmtlZCA9IHRvdGFsQ291bnQgPT0gMyAmJiBhY3RpdmVDb3VudCA9PSAyICYmIGluYWN0aXZlQ291bnQgPT0gMTsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0LmFueVdvcmtlZCA9IGhhc0l0ZW1zICYmIGhhc0FjdGl2ZSAmJiAhaGFzQWRtaW5Sb2xlICYmICFlbXB0eUhhc0l0ZW1zOwoKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgICAgIH0KICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGV4KQogICAgICAgIHsKICAgICAgICAgICAgTG9nRXJyb3IoJCJMaXN0RmlsdGVyL0NvdW50L0FueSBUZXN0IC0gRmFpbGVkOiB7ZXguTWVzc2FnZX0iKTsKICAgICAgICAgICAgZHluYW1pYyBlcnJSZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgICAgICBlcnJSZXN1bHQuZXJyb3IgPSBleC5NZXNzYWdlOwogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSBlcnJSZXN1bHQgfSk7CiAgICAgICAgfQogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYzsKdXNpbmcgU3lzdGVtLkR5bmFtaWM7CnVzaW5nIFN5c3RlbS5MaW5xOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CgpwdWJsaWMgY2xhc3MgTGlzdEZpbHRlckNvdW50QW55TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdHJ5CiAgICAgICAgewogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiTGlzdEZpbHRlciwgTGlzdENvdW50LCBMaXN0QW55IFRlc3QgLSBTdGFydGluZyIpOwoKICAgICAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgICAgIHZhciBpdGVtcyA9IEdldExpc3QoZGF0YSwgIml0ZW1zIik7CgogICAgICAgICAgICB2YXIgYWN0aXZlSXRlbXMgPSBMaXN0RmlsdGVyKGl0ZW1zLCAoRnVuYzxvYmplY3QsIGJvb2w+KSh4ID0+IEdldFByb3BlcnR5VmFsdWUoeCwgInN0YXR1cyIpPy5Ub1N0cmluZygpID09ICJhY3RpdmUiKSk7CiAgICAgICAgICAgIHZhciB0b3RhbENvdW50ID0gTGlzdENvdW50KGl0ZW1zKTsKICAgICAgICAgICAgdmFyIGFjdGl2ZUNvdW50ID0gTGlzdENvdW50KGl0ZW1zLCAoRnVuYzxvYmplY3QsIGJvb2w+KSh4ID0+IEdldFByb3BlcnR5VmFsdWUoeCwgInN0YXR1cyIpPy5Ub1N0cmluZygpID09ICJhY3RpdmUiKSk7CiAgICAgICAgICAgIHZhciBpbmFjdGl2ZUNvdW50ID0gTGlzdENvdW50KGl0ZW1zLCAoRnVuYzxvYmplY3QsIGJvb2w+KSh4ID0+IEdldFByb3BlcnR5VmFsdWUoeCwgInN0YXR1cyIpPy5Ub1N0cmluZygpID09ICJpbmFjdGl2ZSIpKTsKICAgICAgICAgICAgdmFyIGhhc0l0ZW1zID0gTGlzdEFueShpdGVtcyk7CiAgICAgICAgICAgIHZhciBoYXNBY3RpdmUgPSBMaXN0QW55KGl0ZW1zLCAoRnVuYzxvYmplY3QsIGJvb2w+KSh4ID0+IEdldFByb3BlcnR5VmFsdWUoeCwgInN0YXR1cyIpPy5Ub1N0cmluZygpID09ICJhY3RpdmUiKSk7CiAgICAgICAgICAgIHZhciBoYXNBZG1pblJvbGUgPSBMaXN0QW55KGl0ZW1zLCAoRnVuYzxvYmplY3QsIGJvb2w+KSh4ID0+IEdldFByb3BlcnR5VmFsdWUoeCwgInN0YXR1cyIpPy5Ub1N0cmluZygpID09ICJhZG1pbiIpKTsKCiAgICAgICAgICAgIHZhciBlbXB0eUxpc3QgPSBDcmVhdGVMaXN0KCk7CiAgICAgICAgICAgIHZhciBlbXB0eUhhc0l0ZW1zID0gTGlzdEFueShlbXB0eUxpc3QpOwogICAgICAgICAgICB2YXIgZW1wdHlDb3VudCA9IExpc3RDb3VudChlbXB0eUxpc3QpOwoKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oCiAgICAgICAgICAgICAgICAkIkxpc3RGaWx0ZXI6IGFjdGl2ZT17YWN0aXZlQ291bnR9LCBpbmFjdGl2ZT17aW5hY3RpdmVDb3VudH0sIHRvdGFsPXt0b3RhbENvdW50fSIKICAgICAgICAgICAgKTsKCiAgICAgICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpCiAgICAgICAgICAgICAgICByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuc3RhcnRlZEF0ID0gZGF0YS5zdGFydGVkQXQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaXRlbXMiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5pdGVtcyA9IGRhdGEuaXRlbXM7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAibWV0YWRhdGEiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5tZXRhZGF0YSA9IGRhdGEubWV0YWRhdGE7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3JlYXRlQW5kU2V0UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuY3JlYXRlQW5kU2V0UmVzdWx0ID0gZGF0YS5jcmVhdGVBbmRTZXRSZXN1bHQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0TGlzdFJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmdldExpc3RSZXN1bHQgPSBkYXRhLmdldExpc3RSZXN1bHQ7CgogICAgICAgICAgICByZXN1bHQuZmlsdGVyQ291bnRBbnlSZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgICAgICByZXN1bHQuZmlsdGVyQ291bnRBbnlSZXN1bHQuc3VjY2VzcyA9IHRydWU7CiAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdC50b3RhbENvdW50ID0gdG90YWxDb3VudDsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0LmFjdGl2ZUNvdW50ID0gYWN0aXZlQ291bnQ7CiAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdC5pbmFjdGl2ZUNvdW50ID0gaW5hY3RpdmVDb3VudDsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0LmFjdGl2ZUl0ZW1zRmlsdGVyZWQgPSBhY3RpdmVJdGVtcy5Db3VudDsKICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0Lmhhc0l0ZW1zID0gaGFzSXRlbXM7CiAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdC5oYXNBY3RpdmUgPSBoYXNBY3RpdmU7CiAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdC5oYXNBZG1pblJvbGUgPSBoYXNBZG1pblJvbGU7CiAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdC5lbXB0eUxpc3RIYXNJdGVtcyA9IGVtcHR5SGFzSXRlbXM7CiAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdC5lbXB0eUxpc3RDb3VudCA9IGVtcHR5Q291bnQ7CiAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdC5maWx0ZXJXb3JrZWQgPSBhY3RpdmVJdGVtcy5Db3VudCA9PSAyOwogICAgICAgICAgICByZXN1bHQuZmlsdGVyQ291bnRBbnlSZXN1bHQuY291bnRXb3JrZWQgPQogICAgICAgICAgICAgICAgdG90YWxDb3VudCA9PSAzICYmIGFjdGl2ZUNvdW50ID09IDIgJiYgaW5hY3RpdmVDb3VudCA9PSAxOwogICAgICAgICAgICByZXN1bHQuZmlsdGVyQ291bnRBbnlSZXN1bHQuYW55V29ya2VkID0KICAgICAgICAgICAgICAgIGhhc0l0ZW1zICYmIGhhc0FjdGl2ZSAmJiAhaGFzQWRtaW5Sb2xlICYmICFlbXB0eUhhc0l0ZW1zOwoKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgICAgIH0KICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGV4KQogICAgICAgIHsKICAgICAgICAgICAgTG9nRXJyb3IoJCJMaXN0RmlsdGVyL0NvdW50L0FueSBUZXN0IC0gRmFpbGVkOiB7ZXguTWVzc2FnZX0iKTsKICAgICAgICAgICAgZHluYW1pYyBlcnJSZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgICAgICBlcnJSZXN1bHQuZXJyb3IgPSBleC5NZXNzYWdlOwogICAgICAgICAgICBlcnJSZXN1bHQuZXJyb3JTdGFjayA9IGV4LlN0YWNrVHJhY2U7CiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IGVyclJlc3VsdCB9KTsKICAgICAgICB9CiAgICB9Cn0K" } } ], @@ -247,7 +285,7 @@ }, "mapping": { "location": "./src/ListFirstLastMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIExpc3RGaXJzdExhc3RNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJMaXN0Rmlyc3QgYW5kIExpc3RMYXN0IFRlc3QgLSBTdGFydGluZyIpOwoKICAgICAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgICAgIHZhciBpdGVtcyA9IEdldExpc3QoZGF0YSwgIml0ZW1zIik7CgogICAgICAgICAgICB2YXIgZmlyc3QgPSBMaXN0Rmlyc3QoaXRlbXMpOwogICAgICAgICAgICB2YXIgZmlyc3RBY3RpdmUgPSBMaXN0Rmlyc3QoaXRlbXMsIHggPT4geC5zdGF0dXMgPT0gImFjdGl2ZSIpOwogICAgICAgICAgICB2YXIgZmlyc3RJbmFjdGl2ZSA9IExpc3RGaXJzdChpdGVtcywgeCA9PiB4LnN0YXR1cyA9PSAiaW5hY3RpdmUiKTsKICAgICAgICAgICAgdmFyIGZpcnN0Tm90Rm91bmQgPSBMaXN0Rmlyc3QoaXRlbXMsIHggPT4geC5zdGF0dXMgPT0gImFkbWluIik7CgogICAgICAgICAgICB2YXIgbGFzdCA9IExpc3RMYXN0KGl0ZW1zKTsKICAgICAgICAgICAgdmFyIGxhc3RBY3RpdmUgPSBMaXN0TGFzdChpdGVtcywgeCA9PiB4LnN0YXR1cyA9PSAiYWN0aXZlIik7CgogICAgICAgICAgICB2YXIgZW1wdHlGaXJzdCA9IExpc3RGaXJzdChDcmVhdGVMaXN0KCkpOwogICAgICAgICAgICB2YXIgZW1wdHlMYXN0ID0gTGlzdExhc3QoQ3JlYXRlTGlzdCgpKTsKCiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCQiRmlyc3Q6IHtmaXJzdD8ubmFtZX0sIExhc3Q6IHtsYXN0Py5uYW1lfSIpOwoKICAgICAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkQXQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpdGVtcyIpKQogICAgICAgICAgICAgICAgcmVzdWx0Lml0ZW1zID0gZGF0YS5pdGVtczsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJtZXRhZGF0YSIpKQogICAgICAgICAgICAgICAgcmVzdWx0Lm1ldGFkYXRhID0gZGF0YS5tZXRhZGF0YTsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjcmVhdGVBbmRTZXRSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5jcmVhdGVBbmRTZXRSZXN1bHQgPSBkYXRhLmNyZWF0ZUFuZFNldFJlc3VsdDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJnZXRMaXN0UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuZ2V0TGlzdFJlc3VsdCA9IGRhdGEuZ2V0TGlzdFJlc3VsdDsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJmaWx0ZXJDb3VudEFueVJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmZpbHRlckNvdW50QW55UmVzdWx0ID0gZGF0YS5maWx0ZXJDb3VudEFueVJlc3VsdDsKCiAgICAgICAgICAgIHJlc3VsdC5maXJzdExhc3RSZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgICAgICByZXN1bHQuZmlyc3RMYXN0UmVzdWx0LnN1Y2Nlc3MgPSB0cnVlOwogICAgICAgICAgICByZXN1bHQuZmlyc3RMYXN0UmVzdWx0LmZpcnN0TmFtZSA9IChzdHJpbmcpKGZpcnN0Py5uYW1lID8/ICJudWxsIik7CiAgICAgICAgICAgIHJlc3VsdC5maXJzdExhc3RSZXN1bHQubGFzdE5hbWUgPSAoc3RyaW5nKShsYXN0Py5uYW1lID8/ICJudWxsIik7CiAgICAgICAgICAgIHJlc3VsdC5maXJzdExhc3RSZXN1bHQuZmlyc3RBY3RpdmVOYW1lID0gKHN0cmluZykoZmlyc3RBY3RpdmU/Lm5hbWUgPz8gIm51bGwiKTsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5maXJzdEluYWN0aXZlTmFtZSA9IChzdHJpbmcpKGZpcnN0SW5hY3RpdmU/Lm5hbWUgPz8gIm51bGwiKTsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5sYXN0QWN0aXZlTmFtZSA9IChzdHJpbmcpKGxhc3RBY3RpdmU/Lm5hbWUgPz8gIm51bGwiKTsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5maXJzdE5vdEZvdW5kSXNOdWxsID0gZmlyc3ROb3RGb3VuZCA9PSBudWxsOwogICAgICAgICAgICByZXN1bHQuZmlyc3RMYXN0UmVzdWx0LmVtcHR5Rmlyc3RJc051bGwgPSBlbXB0eUZpcnN0ID09IG51bGw7CiAgICAgICAgICAgIHJlc3VsdC5maXJzdExhc3RSZXN1bHQuZW1wdHlMYXN0SXNOdWxsID0gZW1wdHlMYXN0ID09IG51bGw7CiAgICAgICAgICAgIHJlc3VsdC5maXJzdExhc3RSZXN1bHQuZmlyc3RXb3JrZWQgPSBmaXJzdD8uaWQgPT0gIml0ZW0tMDAxIjsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5sYXN0V29ya2VkID0gbGFzdD8uaWQgPT0gIml0ZW0tMDAzIjsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5maXJzdFdpdGhQcmVkaWNhdGVXb3JrZWQgPSBmaXJzdEFjdGl2ZT8uaWQgPT0gIml0ZW0tMDAxIjsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5sYXN0V2l0aFByZWRpY2F0ZVdvcmtlZCA9IGxhc3RBY3RpdmU/LmlkID09ICJpdGVtLTAwMyI7CgogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICBMb2dFcnJvcigkIkxpc3RGaXJzdC9MYXN0IFRlc3QgLSBGYWlsZWQ6IHtleC5NZXNzYWdlfSIpOwogICAgICAgICAgICBkeW5hbWljIGVyclJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgICAgIGVyclJlc3VsdC5lcnJvciA9IGV4Lk1lc3NhZ2U7CiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IGVyclJlc3VsdCB9KTsKICAgICAgICB9CiAgICB9Cn0K" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIExpc3RGaXJzdExhc3RNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJMaXN0Rmlyc3QgYW5kIExpc3RMYXN0IFRlc3QgLSBTdGFydGluZyIpOwoKICAgICAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgICAgIHZhciBpdGVtcyA9IEdldExpc3QoZGF0YSwgIml0ZW1zIik7CgogICAgICAgICAgICB2YXIgZmlyc3QgPSBMaXN0Rmlyc3QoaXRlbXMpOwogICAgICAgICAgICB2YXIgZmlyc3RBY3RpdmUgPSBMaXN0Rmlyc3QoaXRlbXMsIChGdW5jPG9iamVjdCwgYm9vbD4pKHggPT4gR2V0UHJvcGVydHlWYWx1ZSh4LCAic3RhdHVzIik/LlRvU3RyaW5nKCkgPT0gImFjdGl2ZSIpKTsKICAgICAgICAgICAgdmFyIGZpcnN0SW5hY3RpdmUgPSBMaXN0Rmlyc3QoaXRlbXMsIChGdW5jPG9iamVjdCwgYm9vbD4pKHggPT4gR2V0UHJvcGVydHlWYWx1ZSh4LCAic3RhdHVzIik/LlRvU3RyaW5nKCkgPT0gImluYWN0aXZlIikpOwogICAgICAgICAgICB2YXIgZmlyc3ROb3RGb3VuZCA9IExpc3RGaXJzdChpdGVtcywgKEZ1bmM8b2JqZWN0LCBib29sPikoeCA9PiBHZXRQcm9wZXJ0eVZhbHVlKHgsICJzdGF0dXMiKT8uVG9TdHJpbmcoKSA9PSAiYWRtaW4iKSk7CgogICAgICAgICAgICB2YXIgbGFzdCA9IExpc3RMYXN0KGl0ZW1zKTsKICAgICAgICAgICAgdmFyIGxhc3RBY3RpdmUgPSBMaXN0TGFzdChpdGVtcywgKEZ1bmM8b2JqZWN0LCBib29sPikoeCA9PiBHZXRQcm9wZXJ0eVZhbHVlKHgsICJzdGF0dXMiKT8uVG9TdHJpbmcoKSA9PSAiYWN0aXZlIikpOwoKICAgICAgICAgICAgdmFyIGVtcHR5Rmlyc3QgPSBMaXN0Rmlyc3QoQ3JlYXRlTGlzdCgpKTsKICAgICAgICAgICAgdmFyIGVtcHR5TGFzdCA9IExpc3RMYXN0KENyZWF0ZUxpc3QoKSk7CgogICAgICAgICAgICB2YXIgZmlyc3ROYW1lID0gZmlyc3QgIT0gbnVsbCA/IEdldFByb3BlcnR5VmFsdWUoZmlyc3QsICJuYW1lIik/LlRvU3RyaW5nKCkgOiBudWxsOwogICAgICAgICAgICB2YXIgbGFzdE5hbWUgPSBsYXN0ICE9IG51bGwgPyBHZXRQcm9wZXJ0eVZhbHVlKGxhc3QsICJuYW1lIik/LlRvU3RyaW5nKCkgOiBudWxsOwoKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oJCJGaXJzdDoge2ZpcnN0TmFtZX0sIExhc3Q6IHtsYXN0TmFtZX0iKTsKCiAgICAgICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpCiAgICAgICAgICAgICAgICByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuc3RhcnRlZEF0ID0gZGF0YS5zdGFydGVkQXQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaXRlbXMiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5pdGVtcyA9IGRhdGEuaXRlbXM7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAibWV0YWRhdGEiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5tZXRhZGF0YSA9IGRhdGEubWV0YWRhdGE7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3JlYXRlQW5kU2V0UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuY3JlYXRlQW5kU2V0UmVzdWx0ID0gZGF0YS5jcmVhdGVBbmRTZXRSZXN1bHQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0TGlzdFJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmdldExpc3RSZXN1bHQgPSBkYXRhLmdldExpc3RSZXN1bHQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZmlsdGVyQ291bnRBbnlSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdCA9IGRhdGEuZmlsdGVyQ291bnRBbnlSZXN1bHQ7CgogICAgICAgICAgICB2YXIgZmlyc3RJZCA9IGZpcnN0ICE9IG51bGwgPyBHZXRQcm9wZXJ0eVZhbHVlKGZpcnN0LCAiaWQiKT8uVG9TdHJpbmcoKSA6IG51bGw7CiAgICAgICAgICAgIHZhciBsYXN0SWQgPSBsYXN0ICE9IG51bGwgPyBHZXRQcm9wZXJ0eVZhbHVlKGxhc3QsICJpZCIpPy5Ub1N0cmluZygpIDogbnVsbDsKICAgICAgICAgICAgdmFyIGZpcnN0QWN0aXZlSWQgPSBmaXJzdEFjdGl2ZSAhPSBudWxsID8gR2V0UHJvcGVydHlWYWx1ZShmaXJzdEFjdGl2ZSwgImlkIik/LlRvU3RyaW5nKCkgOiBudWxsOwogICAgICAgICAgICB2YXIgZmlyc3RBY3RpdmVOYW1lID0gZmlyc3RBY3RpdmUgIT0gbnVsbCA/IEdldFByb3BlcnR5VmFsdWUoZmlyc3RBY3RpdmUsICJuYW1lIik/LlRvU3RyaW5nKCkgOiBudWxsOwogICAgICAgICAgICB2YXIgZmlyc3RJbmFjdGl2ZU5hbWUgPSBmaXJzdEluYWN0aXZlICE9IG51bGwgPyBHZXRQcm9wZXJ0eVZhbHVlKGZpcnN0SW5hY3RpdmUsICJuYW1lIik/LlRvU3RyaW5nKCkgOiBudWxsOwogICAgICAgICAgICB2YXIgbGFzdEFjdGl2ZUlkID0gbGFzdEFjdGl2ZSAhPSBudWxsID8gR2V0UHJvcGVydHlWYWx1ZShsYXN0QWN0aXZlLCAiaWQiKT8uVG9TdHJpbmcoKSA6IG51bGw7CiAgICAgICAgICAgIHZhciBsYXN0QWN0aXZlTmFtZSA9IGxhc3RBY3RpdmUgIT0gbnVsbCA/IEdldFByb3BlcnR5VmFsdWUobGFzdEFjdGl2ZSwgIm5hbWUiKT8uVG9TdHJpbmcoKSA6IG51bGw7CgogICAgICAgICAgICByZXN1bHQuZmlyc3RMYXN0UmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5zdWNjZXNzID0gdHJ1ZTsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5maXJzdE5hbWUgPSBmaXJzdE5hbWUgPz8gIm51bGwiOwogICAgICAgICAgICByZXN1bHQuZmlyc3RMYXN0UmVzdWx0Lmxhc3ROYW1lID0gbGFzdE5hbWUgPz8gIm51bGwiOwogICAgICAgICAgICByZXN1bHQuZmlyc3RMYXN0UmVzdWx0LmZpcnN0QWN0aXZlTmFtZSA9IGZpcnN0QWN0aXZlTmFtZSA/PyAibnVsbCI7CiAgICAgICAgICAgIHJlc3VsdC5maXJzdExhc3RSZXN1bHQuZmlyc3RJbmFjdGl2ZU5hbWUgPSBmaXJzdEluYWN0aXZlTmFtZSA/PyAibnVsbCI7CiAgICAgICAgICAgIHJlc3VsdC5maXJzdExhc3RSZXN1bHQubGFzdEFjdGl2ZU5hbWUgPSBsYXN0QWN0aXZlTmFtZSA/PyAibnVsbCI7CiAgICAgICAgICAgIHJlc3VsdC5maXJzdExhc3RSZXN1bHQuZmlyc3ROb3RGb3VuZElzTnVsbCA9IGZpcnN0Tm90Rm91bmQgPT0gbnVsbDsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5lbXB0eUZpcnN0SXNOdWxsID0gZW1wdHlGaXJzdCA9PSBudWxsOwogICAgICAgICAgICByZXN1bHQuZmlyc3RMYXN0UmVzdWx0LmVtcHR5TGFzdElzTnVsbCA9IGVtcHR5TGFzdCA9PSBudWxsOwogICAgICAgICAgICByZXN1bHQuZmlyc3RMYXN0UmVzdWx0LmZpcnN0V29ya2VkID0gZmlyc3RJZCA9PSAiaXRlbS0wMDEiOwogICAgICAgICAgICByZXN1bHQuZmlyc3RMYXN0UmVzdWx0Lmxhc3RXb3JrZWQgPSBsYXN0SWQgPT0gIml0ZW0tMDAzIjsKICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdC5maXJzdFdpdGhQcmVkaWNhdGVXb3JrZWQgPSBmaXJzdEFjdGl2ZUlkID09ICJpdGVtLTAwMSI7CiAgICAgICAgICAgIHJlc3VsdC5maXJzdExhc3RSZXN1bHQubGFzdFdpdGhQcmVkaWNhdGVXb3JrZWQgPSBsYXN0QWN0aXZlSWQgPT0gIml0ZW0tMDAzIjsKCiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCQiTGlzdEZpcnN0L0xhc3QgVGVzdCAtIEZhaWxlZDoge2V4Lk1lc3NhZ2V9Iik7CiAgICAgICAgICAgIGR5bmFtaWMgZXJyUmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICAgICAgZXJyUmVzdWx0LmVycm9yID0gZXguTWVzc2FnZTsKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gZXJyUmVzdWx0IH0pOwogICAgICAgIH0KICAgIH0KfQo=" } } ], @@ -299,7 +337,7 @@ }, "mapping": { "location": "./src/ListSelectMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIExpc3RTZWxlY3RNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJMaXN0U2VsZWN0IFRlc3QgLSBTdGFydGluZyIpOwoKICAgICAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgICAgIHZhciBpdGVtcyA9IEdldExpc3QoZGF0YSwgIml0ZW1zIik7CgogICAgICAgICAgICB2YXIgbmFtZXMgPSBMaXN0U2VsZWN0PHN0cmluZz4oaXRlbXMsIHggPT4gKHN0cmluZyl4Lm5hbWUpOwogICAgICAgICAgICB2YXIgYWdlcyA9IExpc3RTZWxlY3Q8aW50PihpdGVtcywgeCA9PiAoaW50KXguYWdlKTsKICAgICAgICAgICAgdmFyIGxhYmVscyA9IExpc3RTZWxlY3Q8c3RyaW5nPihpdGVtcywgeCA9PiAkInt4Lm5hbWV9ICh7eC5zdGF0dXN9KSIpOwogICAgICAgICAgICB2YXIgZW1wdHlSZXN1bHQgPSBMaXN0U2VsZWN0PHN0cmluZz4oQ3JlYXRlTGlzdCgpLCB4ID0+IChzdHJpbmcpeC5uYW1lKTsKCiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCQiTGlzdFNlbGVjdDoge25hbWVzLkNvdW50fSBuYW1lcywge2FnZXMuQ291bnR9IGFnZXMiKTsKCiAgICAgICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpCiAgICAgICAgICAgICAgICByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuc3RhcnRlZEF0ID0gZGF0YS5zdGFydGVkQXQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaXRlbXMiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5pdGVtcyA9IGRhdGEuaXRlbXM7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAibWV0YWRhdGEiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5tZXRhZGF0YSA9IGRhdGEubWV0YWRhdGE7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3JlYXRlQW5kU2V0UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuY3JlYXRlQW5kU2V0UmVzdWx0ID0gZGF0YS5jcmVhdGVBbmRTZXRSZXN1bHQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0TGlzdFJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmdldExpc3RSZXN1bHQgPSBkYXRhLmdldExpc3RSZXN1bHQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZmlsdGVyQ291bnRBbnlSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdCA9IGRhdGEuZmlsdGVyQ291bnRBbnlSZXN1bHQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZmlyc3RMYXN0UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuZmlyc3RMYXN0UmVzdWx0ID0gZGF0YS5maXJzdExhc3RSZXN1bHQ7CgogICAgICAgICAgICByZXN1bHQubGlzdFNlbGVjdFJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgICAgIHJlc3VsdC5saXN0U2VsZWN0UmVzdWx0LnN1Y2Nlc3MgPSB0cnVlOwogICAgICAgICAgICByZXN1bHQubGlzdFNlbGVjdFJlc3VsdC5uYW1lcyA9IG5hbWVzOwogICAgICAgICAgICByZXN1bHQubGlzdFNlbGVjdFJlc3VsdC5hZ2VzID0gYWdlczsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RTZWxlY3RSZXN1bHQubGFiZWxzID0gbGFiZWxzOwogICAgICAgICAgICByZXN1bHQubGlzdFNlbGVjdFJlc3VsdC5uYW1lc0NvdW50ID0gbmFtZXMuQ291bnQ7CiAgICAgICAgICAgIHJlc3VsdC5saXN0U2VsZWN0UmVzdWx0LmFnZXNDb3VudCA9IGFnZXMuQ291bnQ7CiAgICAgICAgICAgIHJlc3VsdC5saXN0U2VsZWN0UmVzdWx0LmVtcHR5U2VsZWN0Q291bnQgPSBlbXB0eVJlc3VsdC5Db3VudDsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RTZWxlY3RSZXN1bHQuc2VsZWN0U3RyaW5nV29ya2VkID0gbmFtZXMuQ291bnQgPT0gMyAmJiBuYW1lc1swXSA9PSAiQWxpY2UiOwogICAgICAgICAgICByZXN1bHQubGlzdFNlbGVjdFJlc3VsdC5zZWxlY3RJbnRXb3JrZWQgPSBhZ2VzLkNvdW50ID09IDMgJiYgYWdlc1swXSA9PSAzMDsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RTZWxlY3RSZXN1bHQuc2VsZWN0VHJhbnNmb3JtV29ya2VkID0gbGFiZWxzLkNvdW50ID09IDM7CiAgICAgICAgICAgIHJlc3VsdC5saXN0U2VsZWN0UmVzdWx0LmVtcHR5U2VsZWN0V29ya2VkID0gZW1wdHlSZXN1bHQuQ291bnQgPT0gMDsKCiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCQiTGlzdFNlbGVjdCBUZXN0IC0gRmFpbGVkOiB7ZXguTWVzc2FnZX0iKTsKICAgICAgICAgICAgZHluYW1pYyBlcnJSZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgICAgICBlcnJSZXN1bHQuZXJyb3IgPSBleC5NZXNzYWdlOwogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSBlcnJSZXN1bHQgfSk7CiAgICAgICAgfQogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIExpc3RTZWxlY3RNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJMaXN0U2VsZWN0IFRlc3QgLSBTdGFydGluZyIpOwoKICAgICAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgICAgIHZhciBpdGVtcyA9IEdldExpc3QoZGF0YSwgIml0ZW1zIik7CgogICAgICAgICAgICB2YXIgbmFtZXMgPSBMaXN0U2VsZWN0PHN0cmluZz4oaXRlbXMsIChGdW5jPG9iamVjdCwgc3RyaW5nPikoeCA9PiBHZXRQcm9wZXJ0eVZhbHVlKHgsICJuYW1lIik/LlRvU3RyaW5nKCkpKTsKICAgICAgICAgICAgdmFyIGFnZXMgPSBMaXN0U2VsZWN0PGludD4oaXRlbXMsIChGdW5jPG9iamVjdCwgaW50PikoeCA9PiB7CiAgICAgICAgICAgICAgICB2YXIgdmFsID0gR2V0UHJvcGVydHlWYWx1ZSh4LCAiYWdlIik7CiAgICAgICAgICAgICAgICByZXR1cm4gdmFsICE9IG51bGwgPyBDb252ZXJ0LlRvSW50MzIodmFsKSA6IDA7CiAgICAgICAgICAgIH0pKTsKICAgICAgICAgICAgdmFyIGxhYmVscyA9IExpc3RTZWxlY3Q8c3RyaW5nPihpdGVtcywgKEZ1bmM8b2JqZWN0LCBzdHJpbmc+KSh4ID0+ICQie0dldFByb3BlcnR5VmFsdWUoeCwgIm5hbWUiKX0gKHtHZXRQcm9wZXJ0eVZhbHVlKHgsICJzdGF0dXMiKX0pIikpOwogICAgICAgICAgICB2YXIgZW1wdHlSZXN1bHQgPSBMaXN0U2VsZWN0PHN0cmluZz4oQ3JlYXRlTGlzdCgpLCAoRnVuYzxvYmplY3QsIHN0cmluZz4pKHggPT4gR2V0UHJvcGVydHlWYWx1ZSh4LCAibmFtZSIpPy5Ub1N0cmluZygpKSk7CgogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkxpc3RTZWxlY3Q6IHtuYW1lcy5Db3VudH0gbmFtZXMsIHthZ2VzLkNvdW50fSBhZ2VzIik7CgogICAgICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRBdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnN0YXJ0ZWRBdCA9IGRhdGEuc3RhcnRlZEF0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgIml0ZW1zIikpCiAgICAgICAgICAgICAgICByZXN1bHQuaXRlbXMgPSBkYXRhLml0ZW1zOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgIm1ldGFkYXRhIikpCiAgICAgICAgICAgICAgICByZXN1bHQubWV0YWRhdGEgPSBkYXRhLm1ldGFkYXRhOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyZWF0ZUFuZFNldFJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmNyZWF0ZUFuZFNldFJlc3VsdCA9IGRhdGEuY3JlYXRlQW5kU2V0UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldExpc3RSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5nZXRMaXN0UmVzdWx0ID0gZGF0YS5nZXRMaXN0UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImZpbHRlckNvdW50QW55UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuZmlsdGVyQ291bnRBbnlSZXN1bHQgPSBkYXRhLmZpbHRlckNvdW50QW55UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImZpcnN0TGFzdFJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdCA9IGRhdGEuZmlyc3RMYXN0UmVzdWx0OwoKICAgICAgICAgICAgcmVzdWx0Lmxpc3RTZWxlY3RSZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgICAgICByZXN1bHQubGlzdFNlbGVjdFJlc3VsdC5zdWNjZXNzID0gdHJ1ZTsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RTZWxlY3RSZXN1bHQubmFtZXMgPSBuYW1lczsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RTZWxlY3RSZXN1bHQuYWdlcyA9IGFnZXM7CiAgICAgICAgICAgIHJlc3VsdC5saXN0U2VsZWN0UmVzdWx0LmxhYmVscyA9IGxhYmVsczsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RTZWxlY3RSZXN1bHQubmFtZXNDb3VudCA9IG5hbWVzLkNvdW50OwogICAgICAgICAgICByZXN1bHQubGlzdFNlbGVjdFJlc3VsdC5hZ2VzQ291bnQgPSBhZ2VzLkNvdW50OwogICAgICAgICAgICByZXN1bHQubGlzdFNlbGVjdFJlc3VsdC5lbXB0eVNlbGVjdENvdW50ID0gZW1wdHlSZXN1bHQuQ291bnQ7CiAgICAgICAgICAgIHJlc3VsdC5saXN0U2VsZWN0UmVzdWx0LnNlbGVjdFN0cmluZ1dvcmtlZCA9IG5hbWVzLkNvdW50ID09IDMgJiYgbmFtZXNbMF0gPT0gIkFsaWNlIjsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RTZWxlY3RSZXN1bHQuc2VsZWN0SW50V29ya2VkID0gYWdlcy5Db3VudCA9PSAzICYmIGFnZXNbMF0gPT0gMzA7CiAgICAgICAgICAgIHJlc3VsdC5saXN0U2VsZWN0UmVzdWx0LnNlbGVjdFRyYW5zZm9ybVdvcmtlZCA9IGxhYmVscy5Db3VudCA9PSAzOwogICAgICAgICAgICByZXN1bHQubGlzdFNlbGVjdFJlc3VsdC5lbXB0eVNlbGVjdFdvcmtlZCA9IGVtcHR5UmVzdWx0LkNvdW50ID09IDA7CgogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICBMb2dFcnJvcigkIkxpc3RTZWxlY3QgVGVzdCAtIEZhaWxlZDoge2V4Lk1lc3NhZ2V9Iik7CiAgICAgICAgICAgIGR5bmFtaWMgZXJyUmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICAgICAgZXJyUmVzdWx0LmVycm9yID0gZXguTWVzc2FnZTsKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gZXJyUmVzdWx0IH0pOwogICAgICAgIH0KICAgIH0KfQo=" } } ], @@ -351,7 +389,7 @@ }, "mapping": { "location": "./src/ListAddRemoveMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIExpc3RBZGRSZW1vdmVNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJMaXN0QWRkIGFuZCBMaXN0UmVtb3ZlIFRlc3QgLSBTdGFydGluZyIpOwoKICAgICAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgICAgIHZhciBpdGVtcyA9IEdldExpc3QoZGF0YSwgIml0ZW1zIik7CgogICAgICAgICAgICB2YXIgY291bnRCZWZvcmUgPSBMaXN0Q291bnQoaXRlbXMpOwoKICAgICAgICAgICAgZHluYW1pYyBuZXdJdGVtID0gQ3JlYXRlT2JqZWN0KCk7CiAgICAgICAgICAgIFNldFByb3BlcnR5KG5ld0l0ZW0sICJpZCIsICJpdGVtLTAwNCIpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShuZXdJdGVtLCAibmFtZSIsICJEaWFuYSIpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShuZXdJdGVtLCAiYWdlIiwgMjgpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShuZXdJdGVtLCAic3RhdHVzIiwgImFjdGl2ZSIpOwoKICAgICAgICAgICAgTGlzdEFkZChpdGVtcywgbmV3SXRlbSk7CiAgICAgICAgICAgIHZhciBjb3VudEFmdGVyQWRkID0gTGlzdENvdW50KGl0ZW1zKTsKCiAgICAgICAgICAgIHZhciByZW1vdmVkQ291bnQgPSBMaXN0UmVtb3ZlKGl0ZW1zLCB4ID0+IHguc3RhdHVzID09ICJpbmFjdGl2ZSIpOwogICAgICAgICAgICB2YXIgY291bnRBZnRlclJlbW92ZSA9IExpc3RDb3VudChpdGVtcyk7CiAgICAgICAgICAgIHZhciBoYXNJbmFjdGl2ZUFmdGVyUmVtb3ZlID0gTGlzdEFueShpdGVtcywgeCA9PiB4LnN0YXR1cyA9PSAiaW5hY3RpdmUiKTsKCiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCQiQWRkL1JlbW92ZTogYmVmb3JlPXtjb3VudEJlZm9yZX0sIGFmdGVyQWRkPXtjb3VudEFmdGVyQWRkfSwgcmVtb3ZlZD17cmVtb3ZlZENvdW50fSwgYWZ0ZXJSZW1vdmU9e2NvdW50QWZ0ZXJSZW1vdmV9Iik7CgogICAgICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRBdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LnN0YXJ0ZWRBdCA9IGRhdGEuc3RhcnRlZEF0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgIm1ldGFkYXRhIikpCiAgICAgICAgICAgICAgICByZXN1bHQubWV0YWRhdGEgPSBkYXRhLm1ldGFkYXRhOwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNyZWF0ZUFuZFNldFJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmNyZWF0ZUFuZFNldFJlc3VsdCA9IGRhdGEuY3JlYXRlQW5kU2V0UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdldExpc3RSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5nZXRMaXN0UmVzdWx0ID0gZGF0YS5nZXRMaXN0UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImZpbHRlckNvdW50QW55UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuZmlsdGVyQ291bnRBbnlSZXN1bHQgPSBkYXRhLmZpbHRlckNvdW50QW55UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImZpcnN0TGFzdFJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmZpcnN0TGFzdFJlc3VsdCA9IGRhdGEuZmlyc3RMYXN0UmVzdWx0OwogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImxpc3RTZWxlY3RSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5saXN0U2VsZWN0UmVzdWx0ID0gZGF0YS5saXN0U2VsZWN0UmVzdWx0OwoKICAgICAgICAgICAgcmVzdWx0Lml0ZW1zID0gaXRlbXM7CgogICAgICAgICAgICByZXN1bHQubGlzdEFkZFJlbW92ZVJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgICAgIHJlc3VsdC5saXN0QWRkUmVtb3ZlUmVzdWx0LnN1Y2Nlc3MgPSB0cnVlOwogICAgICAgICAgICByZXN1bHQubGlzdEFkZFJlbW92ZVJlc3VsdC5jb3VudEJlZm9yZSA9IGNvdW50QmVmb3JlOwogICAgICAgICAgICByZXN1bHQubGlzdEFkZFJlbW92ZVJlc3VsdC5jb3VudEFmdGVyQWRkID0gY291bnRBZnRlckFkZDsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RBZGRSZW1vdmVSZXN1bHQucmVtb3ZlZENvdW50ID0gcmVtb3ZlZENvdW50OwogICAgICAgICAgICByZXN1bHQubGlzdEFkZFJlbW92ZVJlc3VsdC5jb3VudEFmdGVyUmVtb3ZlID0gY291bnRBZnRlclJlbW92ZTsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RBZGRSZW1vdmVSZXN1bHQuaGFzSW5hY3RpdmVBZnRlclJlbW92ZSA9IGhhc0luYWN0aXZlQWZ0ZXJSZW1vdmU7CiAgICAgICAgICAgIHJlc3VsdC5saXN0QWRkUmVtb3ZlUmVzdWx0LmFkZFdvcmtlZCA9IGNvdW50QWZ0ZXJBZGQgPT0gY291bnRCZWZvcmUgKyAxOwogICAgICAgICAgICByZXN1bHQubGlzdEFkZFJlbW92ZVJlc3VsdC5yZW1vdmVXb3JrZWQgPSByZW1vdmVkQ291bnQgPT0gMSAmJiAhaGFzSW5hY3RpdmVBZnRlclJlbW92ZTsKCiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCQiTGlzdEFkZC9SZW1vdmUgVGVzdCAtIEZhaWxlZDoge2V4Lk1lc3NhZ2V9Iik7CiAgICAgICAgICAgIGR5bmFtaWMgZXJyUmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICAgICAgZXJyUmVzdWx0LmVycm9yID0gZXguTWVzc2FnZTsKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gZXJyUmVzdWx0IH0pOwogICAgICAgIH0KICAgIH0KfQo=" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIExpc3RBZGRSZW1vdmVNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJMaXN0QWRkIGFuZCBMaXN0UmVtb3ZlIFRlc3QgLSBTdGFydGluZyIpOwoKICAgICAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgICAgIHZhciBpdGVtcyA9IEdldExpc3QoZGF0YSwgIml0ZW1zIik7CgogICAgICAgICAgICB2YXIgY291bnRCZWZvcmUgPSBMaXN0Q291bnQoaXRlbXMpOwoKICAgICAgICAgICAgZHluYW1pYyBuZXdJdGVtID0gQ3JlYXRlT2JqZWN0KCk7CiAgICAgICAgICAgIFNldFByb3BlcnR5KG5ld0l0ZW0sICJpZCIsICJpdGVtLTAwNCIpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShuZXdJdGVtLCAibmFtZSIsICJEaWFuYSIpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShuZXdJdGVtLCAiYWdlIiwgMjgpOwogICAgICAgICAgICBTZXRQcm9wZXJ0eShuZXdJdGVtLCAic3RhdHVzIiwgImFjdGl2ZSIpOwoKICAgICAgICAgICAgTGlzdEFkZChpdGVtcywgbmV3SXRlbSk7CiAgICAgICAgICAgIHZhciBjb3VudEFmdGVyQWRkID0gTGlzdENvdW50KGl0ZW1zKTsKCiAgICAgICAgICAgIHZhciByZW1vdmVkQ291bnQgPSBMaXN0UmVtb3ZlKGl0ZW1zLCAoRnVuYzxvYmplY3QsIGJvb2w+KSh4ID0+IEdldFByb3BlcnR5VmFsdWUoeCwgInN0YXR1cyIpPy5Ub1N0cmluZygpID09ICJpbmFjdGl2ZSIpKTsKICAgICAgICAgICAgdmFyIGNvdW50QWZ0ZXJSZW1vdmUgPSBMaXN0Q291bnQoaXRlbXMpOwogICAgICAgICAgICB2YXIgaGFzSW5hY3RpdmVBZnRlclJlbW92ZSA9IExpc3RBbnkoaXRlbXMsIChGdW5jPG9iamVjdCwgYm9vbD4pKHggPT4gR2V0UHJvcGVydHlWYWx1ZSh4LCAic3RhdHVzIik/LlRvU3RyaW5nKCkgPT0gImluYWN0aXZlIikpOwoKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oCiAgICAgICAgICAgICAgICAkIkFkZC9SZW1vdmU6IGJlZm9yZT17Y291bnRCZWZvcmV9LCBhZnRlckFkZD17Y291bnRBZnRlckFkZH0sIHJlbW92ZWQ9e3JlbW92ZWRDb3VudH0sIGFmdGVyUmVtb3ZlPXtjb3VudEFmdGVyUmVtb3ZlfSIKICAgICAgICAgICAgKTsKCiAgICAgICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpCiAgICAgICAgICAgICAgICByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuc3RhcnRlZEF0ID0gZGF0YS5zdGFydGVkQXQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAibWV0YWRhdGEiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5tZXRhZGF0YSA9IGRhdGEubWV0YWRhdGE7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY3JlYXRlQW5kU2V0UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuY3JlYXRlQW5kU2V0UmVzdWx0ID0gZGF0YS5jcmVhdGVBbmRTZXRSZXN1bHQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZ2V0TGlzdFJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0LmdldExpc3RSZXN1bHQgPSBkYXRhLmdldExpc3RSZXN1bHQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZmlsdGVyQ291bnRBbnlSZXN1bHQiKSkKICAgICAgICAgICAgICAgIHJlc3VsdC5maWx0ZXJDb3VudEFueVJlc3VsdCA9IGRhdGEuZmlsdGVyQ291bnRBbnlSZXN1bHQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZmlyc3RMYXN0UmVzdWx0IikpCiAgICAgICAgICAgICAgICByZXN1bHQuZmlyc3RMYXN0UmVzdWx0ID0gZGF0YS5maXJzdExhc3RSZXN1bHQ7CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAibGlzdFNlbGVjdFJlc3VsdCIpKQogICAgICAgICAgICAgICAgcmVzdWx0Lmxpc3RTZWxlY3RSZXN1bHQgPSBkYXRhLmxpc3RTZWxlY3RSZXN1bHQ7CgogICAgICAgICAgICByZXN1bHQuaXRlbXMgPSBpdGVtczsKCiAgICAgICAgICAgIHJlc3VsdC5saXN0QWRkUmVtb3ZlUmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RBZGRSZW1vdmVSZXN1bHQuc3VjY2VzcyA9IHRydWU7CiAgICAgICAgICAgIHJlc3VsdC5saXN0QWRkUmVtb3ZlUmVzdWx0LmNvdW50QmVmb3JlID0gY291bnRCZWZvcmU7CiAgICAgICAgICAgIHJlc3VsdC5saXN0QWRkUmVtb3ZlUmVzdWx0LmNvdW50QWZ0ZXJBZGQgPSBjb3VudEFmdGVyQWRkOwogICAgICAgICAgICByZXN1bHQubGlzdEFkZFJlbW92ZVJlc3VsdC5yZW1vdmVkQ291bnQgPSByZW1vdmVkQ291bnQ7CiAgICAgICAgICAgIHJlc3VsdC5saXN0QWRkUmVtb3ZlUmVzdWx0LmNvdW50QWZ0ZXJSZW1vdmUgPSBjb3VudEFmdGVyUmVtb3ZlOwogICAgICAgICAgICByZXN1bHQubGlzdEFkZFJlbW92ZVJlc3VsdC5oYXNJbmFjdGl2ZUFmdGVyUmVtb3ZlID0gaGFzSW5hY3RpdmVBZnRlclJlbW92ZTsKICAgICAgICAgICAgcmVzdWx0Lmxpc3RBZGRSZW1vdmVSZXN1bHQuYWRkV29ya2VkID0gY291bnRBZnRlckFkZCA9PSBjb3VudEJlZm9yZSArIDE7CiAgICAgICAgICAgIHJlc3VsdC5saXN0QWRkUmVtb3ZlUmVzdWx0LnJlbW92ZVdvcmtlZCA9IHJlbW92ZWRDb3VudCA9PSAxICYmICFoYXNJbmFjdGl2ZUFmdGVyUmVtb3ZlOwoKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgICAgIH0KICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGV4KQogICAgICAgIHsKICAgICAgICAgICAgTG9nRXJyb3IoJCJMaXN0QWRkL1JlbW92ZSBUZXN0IC0gRmFpbGVkOiB7ZXguTWVzc2FnZX0iKTsKICAgICAgICAgICAgZHluYW1pYyBlcnJSZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgICAgICBlcnJSZXN1bHQuZXJyb3IgPSBleC5NZXNzYWdlOwogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSBlcnJSZXN1bHQgfSk7CiAgICAgICAgfQogICAgfQp9Cg==" } } ], diff --git a/core/Workflows/collection-object-test/src/ListAddRemoveMapping.csx b/core/Workflows/collection-object-test/src/ListAddRemoveMapping.csx index b6fb71e..1e01612 100644 --- a/core/Workflows/collection-object-test/src/ListAddRemoveMapping.csx +++ b/core/Workflows/collection-object-test/src/ListAddRemoveMapping.csx @@ -31,11 +31,13 @@ public class ListAddRemoveMapping : ScriptBase, IMapping ListAdd(items, newItem); var countAfterAdd = ListCount(items); - var removedCount = ListRemove(items, x => x.status == "inactive"); + var removedCount = ListRemove(items, (Func)(x => GetPropertyValue(x, "status")?.ToString() == "inactive")); var countAfterRemove = ListCount(items); - var hasInactiveAfterRemove = ListAny(items, x => x.status == "inactive"); + var hasInactiveAfterRemove = ListAny(items, (Func)(x => GetPropertyValue(x, "status")?.ToString() == "inactive")); - LogInformation($"Add/Remove: before={countBefore}, afterAdd={countAfterAdd}, removed={removedCount}, afterRemove={countAfterRemove}"); + LogInformation( + $"Add/Remove: before={countBefore}, afterAdd={countAfterAdd}, removed={removedCount}, afterRemove={countAfterRemove}" + ); dynamic result = new ExpandoObject(); diff --git a/core/Workflows/collection-object-test/src/ListFilterCountAnyMapping.csx b/core/Workflows/collection-object-test/src/ListFilterCountAnyMapping.csx index 2c163bd..1e0edb2 100644 --- a/core/Workflows/collection-object-test/src/ListFilterCountAnyMapping.csx +++ b/core/Workflows/collection-object-test/src/ListFilterCountAnyMapping.csx @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Dynamic; +using System.Linq; using System.Threading.Tasks; using BBT.Workflow.Definitions; using BBT.Workflow.Scripting; @@ -20,19 +22,21 @@ public class ListFilterCountAnyMapping : ScriptBase, IMapping var data = context.Instance.Data; var items = GetList(data, "items"); - var activeItems = ListFilter(items, x => x.status == "active"); + var activeItems = ListFilter(items, (Func)(x => GetPropertyValue(x, "status")?.ToString() == "active")); var totalCount = ListCount(items); - var activeCount = ListCount(items, x => x.status == "active"); - var inactiveCount = ListCount(items, x => x.status == "inactive"); + var activeCount = ListCount(items, (Func)(x => GetPropertyValue(x, "status")?.ToString() == "active")); + var inactiveCount = ListCount(items, (Func)(x => GetPropertyValue(x, "status")?.ToString() == "inactive")); var hasItems = ListAny(items); - var hasActive = ListAny(items, x => x.status == "active"); - var hasAdminRole = ListAny(items, x => x.status == "admin"); + var hasActive = ListAny(items, (Func)(x => GetPropertyValue(x, "status")?.ToString() == "active")); + var hasAdminRole = ListAny(items, (Func)(x => GetPropertyValue(x, "status")?.ToString() == "admin")); var emptyList = CreateList(); var emptyHasItems = ListAny(emptyList); var emptyCount = ListCount(emptyList); - LogInformation($"ListFilter: active={activeCount}, inactive={inactiveCount}, total={totalCount}"); + LogInformation( + $"ListFilter: active={activeCount}, inactive={inactiveCount}, total={totalCount}" + ); dynamic result = new ExpandoObject(); @@ -61,8 +65,10 @@ public class ListFilterCountAnyMapping : ScriptBase, IMapping result.filterCountAnyResult.emptyListHasItems = emptyHasItems; result.filterCountAnyResult.emptyListCount = emptyCount; result.filterCountAnyResult.filterWorked = activeItems.Count == 2; - result.filterCountAnyResult.countWorked = totalCount == 3 && activeCount == 2 && inactiveCount == 1; - result.filterCountAnyResult.anyWorked = hasItems && hasActive && !hasAdminRole && !emptyHasItems; + result.filterCountAnyResult.countWorked = + totalCount == 3 && activeCount == 2 && inactiveCount == 1; + result.filterCountAnyResult.anyWorked = + hasItems && hasActive && !hasAdminRole && !emptyHasItems; return Task.FromResult(new ScriptResponse { Data = result }); } @@ -71,6 +77,7 @@ public class ListFilterCountAnyMapping : ScriptBase, IMapping LogError($"ListFilter/Count/Any Test - Failed: {ex.Message}"); dynamic errResult = new ExpandoObject(); errResult.error = ex.Message; + errResult.errorStack = ex.StackTrace; return Task.FromResult(new ScriptResponse { Data = errResult }); } } diff --git a/core/Workflows/collection-object-test/src/ListFirstLastMapping.csx b/core/Workflows/collection-object-test/src/ListFirstLastMapping.csx index a8543b1..d2f2351 100644 --- a/core/Workflows/collection-object-test/src/ListFirstLastMapping.csx +++ b/core/Workflows/collection-object-test/src/ListFirstLastMapping.csx @@ -21,17 +21,20 @@ public class ListFirstLastMapping : ScriptBase, IMapping var items = GetList(data, "items"); var first = ListFirst(items); - var firstActive = ListFirst(items, x => x.status == "active"); - var firstInactive = ListFirst(items, x => x.status == "inactive"); - var firstNotFound = ListFirst(items, x => x.status == "admin"); + var firstActive = ListFirst(items, (Func)(x => GetPropertyValue(x, "status")?.ToString() == "active")); + var firstInactive = ListFirst(items, (Func)(x => GetPropertyValue(x, "status")?.ToString() == "inactive")); + var firstNotFound = ListFirst(items, (Func)(x => GetPropertyValue(x, "status")?.ToString() == "admin")); var last = ListLast(items); - var lastActive = ListLast(items, x => x.status == "active"); + var lastActive = ListLast(items, (Func)(x => GetPropertyValue(x, "status")?.ToString() == "active")); var emptyFirst = ListFirst(CreateList()); var emptyLast = ListLast(CreateList()); - LogInformation($"First: {first?.name}, Last: {last?.name}"); + var firstName = first != null ? GetPropertyValue(first, "name")?.ToString() : null; + var lastName = last != null ? GetPropertyValue(last, "name")?.ToString() : null; + + LogInformation($"First: {firstName}, Last: {lastName}"); dynamic result = new ExpandoObject(); @@ -50,20 +53,28 @@ public class ListFirstLastMapping : ScriptBase, IMapping if (HasProperty(data, "filterCountAnyResult")) result.filterCountAnyResult = data.filterCountAnyResult; + var firstId = first != null ? GetPropertyValue(first, "id")?.ToString() : null; + var lastId = last != null ? GetPropertyValue(last, "id")?.ToString() : null; + var firstActiveId = firstActive != null ? GetPropertyValue(firstActive, "id")?.ToString() : null; + var firstActiveName = firstActive != null ? GetPropertyValue(firstActive, "name")?.ToString() : null; + var firstInactiveName = firstInactive != null ? GetPropertyValue(firstInactive, "name")?.ToString() : null; + var lastActiveId = lastActive != null ? GetPropertyValue(lastActive, "id")?.ToString() : null; + var lastActiveName = lastActive != null ? GetPropertyValue(lastActive, "name")?.ToString() : null; + result.firstLastResult = new ExpandoObject(); result.firstLastResult.success = true; - result.firstLastResult.firstName = (string)(first?.name ?? "null"); - result.firstLastResult.lastName = (string)(last?.name ?? "null"); - result.firstLastResult.firstActiveName = (string)(firstActive?.name ?? "null"); - result.firstLastResult.firstInactiveName = (string)(firstInactive?.name ?? "null"); - result.firstLastResult.lastActiveName = (string)(lastActive?.name ?? "null"); + result.firstLastResult.firstName = firstName ?? "null"; + result.firstLastResult.lastName = lastName ?? "null"; + result.firstLastResult.firstActiveName = firstActiveName ?? "null"; + result.firstLastResult.firstInactiveName = firstInactiveName ?? "null"; + result.firstLastResult.lastActiveName = lastActiveName ?? "null"; result.firstLastResult.firstNotFoundIsNull = firstNotFound == null; result.firstLastResult.emptyFirstIsNull = emptyFirst == null; result.firstLastResult.emptyLastIsNull = emptyLast == null; - result.firstLastResult.firstWorked = first?.id == "item-001"; - result.firstLastResult.lastWorked = last?.id == "item-003"; - result.firstLastResult.firstWithPredicateWorked = firstActive?.id == "item-001"; - result.firstLastResult.lastWithPredicateWorked = lastActive?.id == "item-003"; + result.firstLastResult.firstWorked = firstId == "item-001"; + result.firstLastResult.lastWorked = lastId == "item-003"; + result.firstLastResult.firstWithPredicateWorked = firstActiveId == "item-001"; + result.firstLastResult.lastWithPredicateWorked = lastActiveId == "item-003"; return Task.FromResult(new ScriptResponse { Data = result }); } diff --git a/core/Workflows/collection-object-test/src/ListSelectMapping.csx b/core/Workflows/collection-object-test/src/ListSelectMapping.csx index a473d05..900e615 100644 --- a/core/Workflows/collection-object-test/src/ListSelectMapping.csx +++ b/core/Workflows/collection-object-test/src/ListSelectMapping.csx @@ -20,10 +20,13 @@ public class ListSelectMapping : ScriptBase, IMapping var data = context.Instance.Data; var items = GetList(data, "items"); - var names = ListSelect(items, x => (string)x.name); - var ages = ListSelect(items, x => (int)x.age); - var labels = ListSelect(items, x => $"{x.name} ({x.status})"); - var emptyResult = ListSelect(CreateList(), x => (string)x.name); + var names = ListSelect(items, (Func)(x => GetPropertyValue(x, "name")?.ToString())); + var ages = ListSelect(items, (Func)(x => { + var val = GetPropertyValue(x, "age"); + return val != null ? Convert.ToInt32(val) : 0; + })); + var labels = ListSelect(items, (Func)(x => $"{GetPropertyValue(x, "name")} ({GetPropertyValue(x, "status")})")); + var emptyResult = ListSelect(CreateList(), (Func)(x => GetPropertyValue(x, "name")?.ToString())); LogInformation($"ListSelect: {names.Count} names, {ages.Count} ages"); diff --git a/tests/Core/Tests/collection-object-test/CollectionObjectTestWorkflowTests.cs b/tests/Core/Tests/collection-object-test/CollectionObjectTestWorkflowTests.cs index a9da17e..f026b17 100644 --- a/tests/Core/Tests/collection-object-test/CollectionObjectTestWorkflowTests.cs +++ b/tests/Core/Tests/collection-object-test/CollectionObjectTestWorkflowTests.cs @@ -31,6 +31,7 @@ public async Task HappyPath_AllScriptBaseApis_CompleteSuccessfully() }; var instanceId = await _wf.StartInstanceIdAsync(body); + await _wf.WaitForStateAsync(instanceId, "test-completed-state", StateTimeout); var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); From 26e6669a4a70ac5483f5ee2b141026ce24a859fa Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Tue, 5 May 2026 14:21:26 +0300 Subject: [PATCH 17/24] view-function-extension test needs a rework. --- .../postman-view-function-extension.json | 153 ++++++++++- ...view-function-extension-test-workflow.http | 37 ++- core.link.json | 2 +- .../global-extension.json | 4 +- .../requested-extension.json | 4 +- .../multi-task-function.json | 16 +- .../single-task-function.json | 4 +- .../src/FunctionMultiTask1Mapping.csx | 3 +- .../src/FunctionMultiTask2Mapping.csx | 16 +- .../src/FunctionOutputMapping.csx | 27 +- .../vfe-script-task-2.json | 16 ++ ...ction-extension-test-workflow.diagram.json | 3 + .../Helpers/WorkflowInstanceTestHelper.cs | 21 ++ .../VfeViewAssertions.cs | 104 ++++++++ .../ViewFunctionExtensionTestWorkflowTests.cs | 241 ++++++++++++++++++ 15 files changed, 585 insertions(+), 66 deletions(-) create mode 100644 core/Tasks/view-function-extension/vfe-script-task-2.json create mode 100644 core/Workflows/view-function-extension/.meta/view-function-extension-test-workflow.diagram.json create mode 100644 tests/Core/Tests/view-function-extension-test-workflow/VfeViewAssertions.cs create mode 100644 tests/Core/Tests/view-function-extension-test-workflow/ViewFunctionExtensionTestWorkflowTests.cs diff --git a/api-tests/view-function-extension/postman-view-function-extension.json b/api-tests/view-function-extension/postman-view-function-extension.json index 9286c43..2b443bd 100644 --- a/api-tests/view-function-extension/postman-view-function-extension.json +++ b/api-tests/view-function-extension/postman-view-function-extension.json @@ -2,7 +2,7 @@ "info": { "_postman_id": "5422afe4-5655-4252-91ff-ecbbd776cb64", "name": "vNext Test - View Function Extension", - "description": "Auto-generated from view-function-extension-test-workflow.http.\nvNext workflow integration test collection for view-function-extension.", + "description": "Auto-generated from view-function-extension-test-workflow.http.\nvNext workflow integration test collection for view-function-extension.\nPath: view-test-state → (auto) multi-view-state → (manual) wizard-state → (manual) completed-state", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "variable": [ @@ -25,16 +25,11 @@ "key": "workflow", "value": "view-function-extension-test-workflow", "type": "string" - }, - { - "key": "mocklabBaseUrl", - "value": "http://localhost:3002", - "type": "string" } ], "item": [ { - "name": "Start workflow (sync)", + "name": "Step 1: Start workflow (sync)", "request": { "method": "POST", "header": [ @@ -93,7 +88,7 @@ ] }, { - "name": "Get state (includes view href when state has a view)", + "name": "Step 2: Get state (should be multi-view-state after auto-transition)", "request": { "method": "GET", "header": [], @@ -118,7 +113,7 @@ "response": [] }, { - "name": "Get view payload for current state (optional transitionKey; platform for rule-based clients)", + "name": "Step 3: Get view payload (HTML view on multi-view-state)", "request": { "method": "GET", "header": [], @@ -149,7 +144,7 @@ "response": [] }, { - "name": "Execute single-task function (instance-scoped)", + "name": "Step 4: Execute single-task function (instance-scoped, script task)", "request": { "method": "GET", "header": [], @@ -174,7 +169,7 @@ "response": [] }, { - "name": "Execute multi-task function (script then HTTP; output handler aggregates)", + "name": "Step 5: Execute multi-task function (two script tasks; output handler aggregates)", "request": { "method": "GET", "header": [], @@ -199,7 +194,7 @@ "response": [] }, { - "name": "Instance data without extension filter (global-extension scope Everywhere may apply)", + "name": "Step 6: Instance data without extension filter", "request": { "method": "GET", "header": [], @@ -224,7 +219,7 @@ "response": [] }, { - "name": "Requested extensions via query (DefinedFlowAndRequested + on-demand)", + "name": "Step 7: Requested extensions via query", "request": { "method": "GET", "header": [], @@ -253,6 +248,136 @@ } }, "response": [] + }, + { + "name": "Step 8: Transition to wizard-state", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/transitions/manual-to-wizard", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{vfeInstanceId}}", + "transitions", + "manual-to-wizard" + ] + }, + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [] + }, + { + "name": "Step 9: Verify wizard-state (stateType 5, at most 1 transition)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{vfeInstanceId}}", + "functions", + "state" + ] + } + }, + "response": [] + }, + { + "name": "Step 10: Complete workflow (complete-with-markdown-view)", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/transitions/complete-with-markdown-view", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{vfeInstanceId}}", + "transitions", + "complete-with-markdown-view" + ] + }, + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [] + }, + { + "name": "Step 11: Verify completed-state with status C", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{vfeInstanceId}}", + "functions", + "state" + ] + } + }, + "response": [] } ] -} \ No newline at end of file +} diff --git a/api-tests/view-function-extension/view-function-extension-test-workflow.http b/api-tests/view-function-extension/view-function-extension-test-workflow.http index 6c277ea..2c168c5 100644 --- a/api-tests/view-function-extension/view-function-extension-test-workflow.http +++ b/api-tests/view-function-extension/view-function-extension-test-workflow.http @@ -2,15 +2,14 @@ @apiVersion = 1 @domain = core @workflow = view-function-extension-test-workflow -@mocklabBaseUrl = http://localhost:3002 ############################################################################### # View, function, and extension integration tests. -# Requires: API with MocklabBaseUrl for HTTP task in multi-task-function; -# Mocklab seeded with GET /api/test/user-info (integration-test-collection). +# Workflow path: view-test-state → (auto) multi-view-state → (manual) +# wizard-state → (manual) completed-state ############################################################################### -### Start workflow (sync) +### Step 1: Start workflow (sync) — lands on view-test-state, auto-transitions to multi-view-state # @name createVfeInstance POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json @@ -23,20 +22,38 @@ Content-Type: application/json @vfeInstanceId = {{createVfeInstance.response.body.$.id}} -### Get state (includes view href when state has a view) +### Step 2: Get state — should be multi-view-state (auto-transition fires immediately) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/state -### Get view payload for current state (optional transitionKey; platform for rule-based clients) +### Step 3: Get view payload (state-level HTML view on multi-view-state, optional platform filter) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/view?platform=web -### Execute single-task function (instance-scoped) +### Step 4: Execute single-task function (instance-scoped, script task) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/single-task-function -### Execute multi-task function (script then HTTP; output handler aggregates) +### Step 5: Execute multi-task function (two script tasks; output handler aggregates) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/multi-task-function -### Instance data without extension filter (global-extension scope Everywhere may apply) +### Step 6: Instance data without extension filter (global-extension scope Everywhere may apply) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/data -### Requested extensions via query (DefinedFlowAndRequested + on-demand) +### Step 7: Requested extensions via query (DefinedFlowAndRequested + on-demand) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/data?extensions=requested-extension,global-extension + +### Step 8: Transition to wizard-state (manual-to-wizard) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/transitions/manual-to-wizard +Content-Type: application/json + +{} + +### Step 9: Verify wizard-state — stateType 5, at most 1 transition, transition has markdown-view +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/state + +### Step 10: Complete workflow (complete-with-markdown-view transition) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/transitions/complete-with-markdown-view +Content-Type: application/json + +{} + +### Step 11: Verify completed-state with status C +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/state diff --git a/core.link.json b/core.link.json index 18bea35..8327ba3 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-05-05T06:32:51.056Z" + "importedAt": "2026-05-05T11:18:04.858Z" } \ No newline at end of file diff --git a/core/Extensions/view-function-extension/global-extension.json b/core/Extensions/view-function-extension/global-extension.json index a940de8..c5627c9 100644 --- a/core/Extensions/view-function-extension/global-extension.json +++ b/core/Extensions/view-function-extension/global-extension.json @@ -21,8 +21,8 @@ "flow": "sys-tasks" }, "mapping": { - "type": "G", - "location": "./src/GlobalExtensionMapping.csx" + "location": "./src/GlobalExtensionMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBHbG9iYWxFeHRlbnNpb25NYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LmV4dGVuc2lvblR5cGUgPSAiZ2xvYmFsIjsKICAgICAgICByZXN1bHQudGltZXN0YW1wID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } } diff --git a/core/Extensions/view-function-extension/requested-extension.json b/core/Extensions/view-function-extension/requested-extension.json index 9309add..dfed388 100644 --- a/core/Extensions/view-function-extension/requested-extension.json +++ b/core/Extensions/view-function-extension/requested-extension.json @@ -21,8 +21,8 @@ "flow": "sys-tasks" }, "mapping": { - "type": "G", - "location": "./src/RequestedExtensionMapping.csx" + "location": "./src/RequestedExtensionMapping.csx", + "code": "dXNpbmcgU3lzdGVtLkR5bmFtaWM7CnVzaW5nIFN5c3RlbS5UaHJlYWRpbmcuVGFza3M7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CgpwdWJsaWMgY2xhc3MgUmVxdWVzdGVkRXh0ZW5zaW9uTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC5leHRlbnNpb25UeXBlID0gInJlcXVlc3RlZCI7CiAgICAgICAgcmVzdWx0Lm9uRGVtYW5kID0gdHJ1ZTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } } diff --git a/core/Functions/view-function-extension/multi-task-function.json b/core/Functions/view-function-extension/multi-task-function.json index 7405e01..9b6682d 100644 --- a/core/Functions/view-function-extension/multi-task-function.json +++ b/core/Functions/view-function-extension/multi-task-function.json @@ -21,27 +21,27 @@ "flow": "sys-tasks" }, "mapping": { - "type": "G", - "location": "./src/FunctionMultiTask1Mapping.csx" + "location": "./src/FunctionMultiTask1Mapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEZ1bmN0aW9uTXVsdGlUYXNrMU1hcHBpbmcgOiBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQuc3RlcCA9IDE7CiAgICAgICAgcmVzdWx0LnZmZVNjcmlwdCA9IHRydWU7CiAgICAgICAgcmVzdWx0LmF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } }, { "order": 2, "task": { - "key": "vfe-http-task", + "key": "vfe-script-task-2", "domain": "core", "version": "1.0.0", "flow": "sys-tasks" }, "mapping": { - "type": "G", - "location": "./src/FunctionMultiTask2Mapping.csx" + "location": "./src/FunctionMultiTask2Mapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIEZ1bmN0aW9uTXVsdGlUYXNrMk1hcHBpbmcgOiBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQuc3RlcCA9IDI7CiAgICAgICAgcmVzdWx0LnZmZVNlY29uZFNjcmlwdCA9IHRydWU7CiAgICAgICAgcmVzdWx0LmF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" } } ], "output": { - "type": "G", - "location": "./src/FunctionOutputMapping.csx" + "location": "./src/FunctionOutputMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIEZ1bmN0aW9uT3V0cHV0TWFwcGluZyA6IElPdXRwdXRIYW5kbGVyCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgdGFzazFSZXN1bHQgPSBjb250ZXh0Lk91dHB1dFJlc3BvbnNlP1sidmZlU2NyaXB0VGFzayJdOwogICAgICAgIHZhciB0YXNrMlJlc3VsdCA9IGNvbnRleHQuT3V0cHV0UmVzcG9uc2U/WyJ2ZmVTY3JpcHRUYXNrMiJdOwoKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgIHsKICAgICAgICAgICAgS2V5ID0gIm11bHRpLXRhc2stb3V0cHV0IiwKICAgICAgICAgICAgRGF0YSA9IG5ldwogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBzY3JpcHRSZXN1bHQgPSB0YXNrMVJlc3VsdCwKICAgICAgICAgICAgICAgIHNlY29uZFJlc3VsdCA9IHRhc2syUmVzdWx0LAogICAgICAgICAgICAgICAgYWdncmVnYXRlZCA9IHRydWUsCiAgICAgICAgICAgICAgICBub3JtYWxpemVkQXQgPSBEYXRlVGltZS5VdGNOb3cKICAgICAgICAgICAgfQogICAgICAgIH0pOwogICAgfQp9Cg==" } } -} +} \ No newline at end of file diff --git a/core/Functions/view-function-extension/single-task-function.json b/core/Functions/view-function-extension/single-task-function.json index 6da0c5b..54f73e5 100644 --- a/core/Functions/view-function-extension/single-task-function.json +++ b/core/Functions/view-function-extension/single-task-function.json @@ -20,8 +20,8 @@ "flow": "sys-tasks" }, "mapping": { - "type": "G", - "location": "./src/FunctionSingleTaskMapping.csx" + "location": "./src/FunctionSingleTaskMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBGdW5jdGlvblNpbmdsZVRhc2tNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnNpbmdsZVRhc2tGdW5jdGlvbiA9IHRydWU7CiAgICAgICAgcmVzdWx0LmV4ZWN1dGVkQXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } } diff --git a/core/Functions/view-function-extension/src/FunctionMultiTask1Mapping.csx b/core/Functions/view-function-extension/src/FunctionMultiTask1Mapping.csx index 016e744..5f62311 100644 --- a/core/Functions/view-function-extension/src/FunctionMultiTask1Mapping.csx +++ b/core/Functions/view-function-extension/src/FunctionMultiTask1Mapping.csx @@ -2,8 +2,9 @@ using System; using System.Dynamic; using System.Threading.Tasks; using BBT.Workflow.Scripting; +using BBT.Workflow.Definitions; -public class FunctionMultiTask1Mapping : ScriptBase, IMapping +public class FunctionMultiTask1Mapping : IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) { diff --git a/core/Functions/view-function-extension/src/FunctionMultiTask2Mapping.csx b/core/Functions/view-function-extension/src/FunctionMultiTask2Mapping.csx index de5d5fb..2c74394 100644 --- a/core/Functions/view-function-extension/src/FunctionMultiTask2Mapping.csx +++ b/core/Functions/view-function-extension/src/FunctionMultiTask2Mapping.csx @@ -3,31 +3,19 @@ using System.Dynamic; using System.Threading.Tasks; using BBT.Workflow.Scripting; using BBT.Workflow.Definitions; -using BBT.Workflow.Scripting.Functions; -public class FunctionMultiTask2Mapping : ScriptBase, IMapping +public class FunctionMultiTask2Mapping : IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) { - var httpTask = task as HttpTask; - if (httpTask != null) - { - string baseUrl = GetConfigValue("MocklabBaseUrl"); - httpTask.Url = httpTask.Url.Replace("{MocklabBaseUrl}", baseUrl); - } return Task.FromResult(new ScriptResponse()); } public Task OutputHandler(ScriptContext context) { - var taskResponse = context.Body; dynamic result = new ExpandoObject(); result.step = 2; - result.vfeHttp = true; - if (taskResponse != null && HasProperty(taskResponse, "statusCode")) - result.statusCode = taskResponse.statusCode; - if (taskResponse != null && HasProperty(taskResponse, "data")) - result.userInfo = taskResponse.data; + result.vfeSecondScript = true; result.at = DateTime.UtcNow.ToString("o"); return Task.FromResult(new ScriptResponse { Data = result }); } diff --git a/core/Functions/view-function-extension/src/FunctionOutputMapping.csx b/core/Functions/view-function-extension/src/FunctionOutputMapping.csx index c8fb185..e53a8c8 100644 --- a/core/Functions/view-function-extension/src/FunctionOutputMapping.csx +++ b/core/Functions/view-function-extension/src/FunctionOutputMapping.csx @@ -1,21 +1,24 @@ -using System.Dynamic; +using System; using System.Threading.Tasks; using BBT.Workflow.Scripting; -public class FunctionOutputMapping : ScriptBase, IOutputHandler +public class FunctionOutputMapping : IOutputHandler { public Task OutputHandler(ScriptContext context) { - dynamic result = new ExpandoObject(); - var outputs = context.OutputResponse; - if (outputs != null) + var task1Result = context.OutputResponse?["vfeScriptTask"]; + var task2Result = context.OutputResponse?["vfeScriptTask2"]; + + return Task.FromResult(new ScriptResponse { - if (HasProperty(outputs, "vfeScriptTask")) - result.scriptResult = outputs.vfeScriptTask; - if (HasProperty(outputs, "vfeHttpTask")) - result.httpResult = outputs.vfeHttpTask; - } - result.aggregated = true; - return Task.FromResult(new ScriptResponse { Data = result }); + Key = "multi-task-output", + Data = new + { + scriptResult = task1Result, + secondResult = task2Result, + aggregated = true, + normalizedAt = DateTime.UtcNow + } + }); } } diff --git a/core/Tasks/view-function-extension/vfe-script-task-2.json b/core/Tasks/view-function-extension/vfe-script-task-2.json new file mode 100644 index 0000000..79f1a38 --- /dev/null +++ b/core/Tasks/view-function-extension/vfe-script-task-2.json @@ -0,0 +1,16 @@ +{ + "key": "vfe-script-task-2", + "version": "1.0.0", + "domain": "core", + "flow": "sys-tasks", + "flowVersion": "1.0.0", + "tags": [ + "integration-test", + "view-function-extension", + "script-task" + ], + "attributes": { + "type": "7", + "config": {} + } +} diff --git a/core/Workflows/view-function-extension/.meta/view-function-extension-test-workflow.diagram.json b/core/Workflows/view-function-extension/.meta/view-function-extension-test-workflow.diagram.json new file mode 100644 index 0000000..00a4467 --- /dev/null +++ b/core/Workflows/view-function-extension/.meta/view-function-extension-test-workflow.diagram.json @@ -0,0 +1,3 @@ +{ + "nodePos": {} +} \ No newline at end of file diff --git a/tests/Core/Helpers/WorkflowInstanceTestHelper.cs b/tests/Core/Helpers/WorkflowInstanceTestHelper.cs index e1263f1..bc166df 100644 --- a/tests/Core/Helpers/WorkflowInstanceTestHelper.cs +++ b/tests/Core/Helpers/WorkflowInstanceTestHelper.cs @@ -161,6 +161,27 @@ public async Task GetAttributesAsync(string instanceId) return attributes; } + /// + /// Calls a named instance function (GET .../functions/{functionName}) and returns the response body. + /// Useful for view, data, custom functions, etc. + /// + public async Task CallFunctionAsync( + string instanceId, + string functionName, + Dictionary? queryParams = null, + Dictionary? headers = null + ) + { + var response = await _api.CallInstanceFunctionAsync( + _workflowKey, + instanceId, + functionName, + queryParams, + headers + ); + return response.Body; + } + /// /// Calls GET .../instances with any subset of filter / sort / page / /// pageSize query parameters and asserts HTTP 200 before returning the response body. diff --git a/tests/Core/Tests/view-function-extension-test-workflow/VfeViewAssertions.cs b/tests/Core/Tests/view-function-extension-test-workflow/VfeViewAssertions.cs new file mode 100644 index 0000000..997e80b --- /dev/null +++ b/tests/Core/Tests/view-function-extension-test-workflow/VfeViewAssertions.cs @@ -0,0 +1,104 @@ +using System.Text.Json; +using Xunit; + +namespace Core.IntegrationTests.Tests.ViewFunctionExtensionTestWorkflow; + +/// +/// View-specific assertions for view-function-extension-test-workflow. +/// Validates that GET .../functions/state responses include view metadata +/// (state-level and transition-level). +/// +public static class VfeViewAssertions +{ + /// + /// Asserts that the state function body indicates the current state has a view + /// (view.hasView == true or view object is present with an href). + /// + public static void AssertStateHasView(JsonElement stateBody) + { + Assert.True( + stateBody.ValueKind == JsonValueKind.Object + && stateBody.TryGetProperty("view", out var viewEl) + && viewEl.ValueKind == JsonValueKind.Object, + "State function body should contain a 'view' object." + ); + + var viewObj = stateBody.GetProperty("view"); + + if (viewObj.TryGetProperty("hasView", out var hasView)) + { + Assert.True( + hasView.ValueKind == JsonValueKind.True, + "view.hasView should be true when state has a bound view." + ); + } + + if (viewObj.TryGetProperty("href", out var href)) + { + Assert.True( + href.ValueKind == JsonValueKind.String + && !string.IsNullOrWhiteSpace(href.GetString()), + "view.href should be a non-empty string." + ); + } + } + + /// + /// Asserts that a specific transition in the state function body has a view reference. + /// + public static void AssertTransitionHasView(JsonElement stateBody, string transitionKey) + { + Assert.True( + stateBody.TryGetProperty("transitions", out var transitions) + && transitions.ValueKind == JsonValueKind.Array, + "State body should have a transitions array." + ); + + JsonElement? matchedTransition = null; + foreach (var t in transitions.EnumerateArray()) + { + if (t.ValueKind != JsonValueKind.Object) + continue; + + string? name = null; + if (t.TryGetProperty("name", out var nameEl) && nameEl.ValueKind == JsonValueKind.String) + name = nameEl.GetString(); + if (name == null && t.TryGetProperty("key", out var keyEl) && keyEl.ValueKind == JsonValueKind.String) + name = keyEl.GetString(); + + if (string.Equals(name, transitionKey, StringComparison.Ordinal)) + { + matchedTransition = t; + break; + } + } + + Assert.True( + matchedTransition.HasValue, + $"Transition '{transitionKey}' should be listed in state transitions." + ); + + var transition = matchedTransition!.Value; + Assert.True( + transition.TryGetProperty("view", out var viewEl) + && viewEl.ValueKind == JsonValueKind.Object, + $"Transition '{transitionKey}' should have a 'view' object." + ); + + if (viewEl.TryGetProperty("href", out var href)) + { + Assert.True( + href.ValueKind == JsonValueKind.String + && !string.IsNullOrWhiteSpace(href.GetString()), + $"Transition '{transitionKey}' view.href should be a non-empty string." + ); + } + else if (viewEl.TryGetProperty("hasView", out var hasView)) + { + Assert.True( + hasView.ValueKind == JsonValueKind.True, + $"Transition '{transitionKey}' view.hasView should be true (or view.href should be present)." + ); + } + } +} diff --git a/tests/Core/Tests/view-function-extension-test-workflow/ViewFunctionExtensionTestWorkflowTests.cs b/tests/Core/Tests/view-function-extension-test-workflow/ViewFunctionExtensionTestWorkflowTests.cs new file mode 100644 index 0000000..d8e3e76 --- /dev/null +++ b/tests/Core/Tests/view-function-extension-test-workflow/ViewFunctionExtensionTestWorkflowTests.cs @@ -0,0 +1,241 @@ +using System.Text.Json; +using Core.IntegrationTests.Helpers; +using Core.IntegrationTests.Infrastructure; + +namespace Core.IntegrationTests.Tests.ViewFunctionExtensionTestWorkflow; + +/// +/// Integration tests for view-function-extension-test-workflow. +/// Covers: view types (JSON/HTML/Markdown), display modes, wizard state (stateType 5), +/// single-task / multi-task functions, global/requested extensions, features reference. +/// Aligned with api-tests/view-function-extension/view-function-extension-test-workflow.http +/// and docs/integration-test-documentation.md Grup 5. +/// +public class ViewFunctionExtensionTestWorkflowTests : IntegrationTestBase +{ + private const string WorkflowKey = "view-function-extension-test-workflow"; + private static readonly TimeSpan ShortTimeout = TimeSpan.FromSeconds(15); + private readonly WorkflowInstanceTestHelper _wf; + + public ViewFunctionExtensionTestWorkflowTests(VNextTestEnvironment environment) + : base(environment) + { + _wf = new WorkflowInstanceTestHelper(Api, WorkflowKey); + } + + // ------------------------------------------------------------------------- + // A. Happy Path — workflow reaches completed-state via manual transitions + // ------------------------------------------------------------------------- + + [Fact] + public async Task HappyPath_ManualTransitions_ReachesCompletedWithStatusC() + { + var instanceId = await StartVfeInstanceAsync(); + + // WebPlatformRule always returns true → auto-to-multi-view fires + await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + + await _wf.RunTransitionAsync(instanceId, "manual-to-wizard", headers: null); + await _wf.AssertStateAsync(instanceId, "wizard-state"); + + await _wf.RunTransitionAsync( + instanceId, + "complete-with-markdown-view", + headers: null + ); + + await _wf.WaitForStateAsync(instanceId, "completed-state", ShortTimeout); + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("C", StateFunctionJson.ExtractStatus(stateBody)); + } + + // ------------------------------------------------------------------------- + // B. View assertions — state-level and transition-level views + // ------------------------------------------------------------------------- + + [Fact] + public async Task ViewTestState_HasView_WithJsonType() + { + var instanceId = await StartVfeInstanceAsync(); + + // multi-view-state reached via auto; but we can query view from any reachable state + // The instance auto-transitions so fast we check multi-view-state view instead + await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + + VfeViewAssertions.AssertStateHasView(stateBody); + } + + [Fact] + public async Task ViewFunction_ReturnsViewPayload() + { + var instanceId = await StartVfeInstanceAsync(); + await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + + var viewBody = await _wf.CallFunctionAsync( + instanceId, + "view", + queryParams: new Dictionary { ["platform"] = "web" } + ); + + Assert.NotEqual(JsonValueKind.Undefined, viewBody.ValueKind); + } + + [Fact] + public async Task WizardState_TransitionView_MarkdownView() + { + var instanceId = await StartVfeInstanceAsync(); + await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + + await _wf.RunTransitionAsync(instanceId, "manual-to-wizard", headers: null); + await _wf.AssertStateAsync(instanceId, "wizard-state"); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + + Assert.True( + StateFunctionJson.TransitionsContainKey(stateBody, "complete-with-markdown-view"), + "Wizard state should list complete-with-markdown-view transition." + ); + + VfeViewAssertions.AssertTransitionHasView(stateBody, "complete-with-markdown-view"); + } + + // ------------------------------------------------------------------------- + // C. Wizard state constraint — stateType 5 allows at most 1 transition + // ------------------------------------------------------------------------- + + [Fact] + public async Task WizardState_HasAtMostOneTransition() + { + var instanceId = await StartVfeInstanceAsync(); + await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + + await _wf.RunTransitionAsync(instanceId, "manual-to-wizard", headers: null); + await _wf.AssertStateAsync(instanceId, "wizard-state"); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + + if ( + stateBody.TryGetProperty("transitions", out var transitions) + && transitions.ValueKind == JsonValueKind.Array + ) + { + Assert.True( + transitions.GetArrayLength() <= 1, + $"Wizard state (stateType 5) should have at most 1 transition; found {transitions.GetArrayLength()}." + ); + } + } + + // ------------------------------------------------------------------------- + // D. Function tests + // ------------------------------------------------------------------------- + + [Fact] + public async Task SingleTaskFunction_ReturnsResponse() + { + var instanceId = await StartVfeInstanceAsync(); + await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + + var functionBody = await _wf.CallFunctionAsync( + instanceId, + "single-task-function" + ); + + Assert.NotEqual(JsonValueKind.Undefined, functionBody.ValueKind); + } + + [Fact] + public async Task MultiTaskFunction_ReturnsAggregatedResponse() + { + var instanceId = await StartVfeInstanceAsync(); + await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + + var functionBody = await _wf.CallFunctionAsync( + instanceId, + "multi-task-function" + ); + + Assert.NotEqual(JsonValueKind.Undefined, functionBody.ValueKind); + } + + // ------------------------------------------------------------------------- + // E. Extension tests + // ------------------------------------------------------------------------- + + [Fact] + public async Task DataFunction_WithoutExtensionFilter_ReturnsInstanceData() + { + var instanceId = await StartVfeInstanceAsync(); + await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + + var dataBody = await _wf.CallFunctionAsync(instanceId, "data"); + + Assert.NotEqual(JsonValueKind.Undefined, dataBody.ValueKind); + } + + [Fact] + public async Task DataFunction_WithRequestedExtensions_ReturnsEnrichedData() + { + var instanceId = await StartVfeInstanceAsync(); + await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + + var dataBody = await _wf.CallFunctionAsync( + instanceId, + "data", + queryParams: new Dictionary + { + ["extensions"] = "requested-extension,global-extension", + } + ); + + Assert.NotEqual(JsonValueKind.Undefined, dataBody.ValueKind); + } + + // ------------------------------------------------------------------------- + // F. Instance attributes — startTransition mapping wrote vfeTestStarted + // ------------------------------------------------------------------------- + + [Fact] + public async Task StartTransitionMapping_SetsVfeTestStarted() + { + var instanceId = await StartVfeInstanceAsync(); + await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + + var attrs = await _wf.GetAttributesAsync(instanceId); + JsonElementAssertions.AssertPropertyTrue( + attrs, + "vfeTestStarted", + "InitVfeMapping should set attributes.vfeTestStarted = true." + ); + } + + // ------------------------------------------------------------------------- + // G. Auto transition rule — WebPlatformRule + default fallback + // ------------------------------------------------------------------------- + + [Fact] + public async Task AutoTransition_WebPlatformRule_ReachesMultiViewState() + { + var instanceId = await StartVfeInstanceAsync(); + + await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + } + + // ------------------------------------------------------------------------- + // Private helpers + // ------------------------------------------------------------------------- + + private async Task StartVfeInstanceAsync() + { + return await _wf.StartInstanceIdAsync( + new + { + key = WorkflowInstanceTestHelper.UniqueInstanceKey("vfe-test"), + tags = new[] { "integration-test", "view-function-extension" }, + attributes = new { }, + } + ); + } +} From b00fdb20258d5c855c518c4a72e673135df9769f Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Wed, 6 May 2026 10:23:39 +0300 Subject: [PATCH 18/24] view-function-extension integration test is completed. wizard state needs a review. --- .../postman-view-function-extension.json | 759 +++++++++++------- ...view-function-extension-test-workflow.http | 136 +++- core.link.json | 2 +- .../global-extension.json | 29 - .../requested-extension.json | 29 - ...efinedFlowAndRequestedExtensionMapping.csx | 21 + .../src/VfeDefinedFlowsExtensionMapping.csx | 21 + .../VfeGlobalAndRequestedExtensionMapping.csx | 21 + .../src/VfeGlobalExtensionMapping.csx | 21 + ...-defined-flow-and-requested-extension.json | 31 + .../vfe-defined-flows-extension.json | 31 + .../vfe-global-and-requested-extension.json | 31 + .../vfe-global-extension.json | 31 + .../src/VfeDomainFunctionMapping.csx} | 7 +- .../src/VfeWorkflowFunctionMapping.csx} | 8 +- .../vfe-domain-function.json | 29 + ...tion.json => vfe-multi-task-function.json} | 5 +- ...ion.json => vfe-single-task-function.json} | 7 +- .../vfe-workflow-function.json | 29 + .../deeplink-view.json | 17 + .../view-function-extension/html-view.json | 4 + .../view-function-extension/http-view.json | 17 + .../view-function-extension/json-view.json | 4 + .../markdown-view.json | 4 + .../view-function-extension/urn-view.json | 17 + .../error-boundary-test-workflow.json | 2 +- core/Workflows/flow-types/core-flow-test.json | 2 +- core/Workflows/flow-types/flow-flow-test.json | 2 +- .../flow-types/subflow-flow-test.json | 2 +- .../flow-types/subprocess-flow-test.json | 2 +- .../instance-management-test-workflow.json | 2 +- .../lifecycle-transitions-test-workflow.json | 2 +- core/Workflows/payments/payment-process.json | 2 +- .../payments/scheduled-payments-workflow.json | 2 +- .../schema-data-test-workflow.json | 2 +- .../subflow-orchestration-child.json | 2 +- .../subflow-orchestration-grandchild.json | 2 +- .../subflow-orchestration-parent.json | 22 +- .../task-execution-test-workflow.json | 2 +- .../version-consistency-test-workflow-v2.json | 2 +- .../version-consistency-test-workflow.json | 2 +- ...ction-extension-test-workflow.diagram.json | 43 +- ...view-function-extension-test-workflow.json | 266 ++++-- docs/integration-test-documentation.md | 227 ++++-- tests/Core/Helpers/DomainFunctionHelper.cs | 33 + .../Helpers/WorkflowInstanceTestHelper.cs | 49 ++ .../VfeExtensionAssertions.cs | 98 +++ .../VfeFunctionAssertions.cs | 88 ++ .../VfeScenarioActions.cs | 105 +++ .../VfeViewAssertions.cs | 99 +++ .../ViewFunctionExtensionTestWorkflowTests.cs | 576 ++++++++++--- 51 files changed, 2301 insertions(+), 646 deletions(-) delete mode 100644 core/Extensions/view-function-extension/global-extension.json delete mode 100644 core/Extensions/view-function-extension/requested-extension.json create mode 100644 core/Extensions/view-function-extension/src/VfeDefinedFlowAndRequestedExtensionMapping.csx create mode 100644 core/Extensions/view-function-extension/src/VfeDefinedFlowsExtensionMapping.csx create mode 100644 core/Extensions/view-function-extension/src/VfeGlobalAndRequestedExtensionMapping.csx create mode 100644 core/Extensions/view-function-extension/src/VfeGlobalExtensionMapping.csx create mode 100644 core/Extensions/view-function-extension/vfe-defined-flow-and-requested-extension.json create mode 100644 core/Extensions/view-function-extension/vfe-defined-flows-extension.json create mode 100644 core/Extensions/view-function-extension/vfe-global-and-requested-extension.json create mode 100644 core/Extensions/view-function-extension/vfe-global-extension.json rename core/{Extensions/view-function-extension/src/GlobalExtensionMapping.csx => Functions/view-function-extension/src/VfeDomainFunctionMapping.csx} (69%) rename core/{Extensions/view-function-extension/src/RequestedExtensionMapping.csx => Functions/view-function-extension/src/VfeWorkflowFunctionMapping.csx} (67%) create mode 100644 core/Functions/view-function-extension/vfe-domain-function.json rename core/Functions/view-function-extension/{multi-task-function.json => vfe-multi-task-function.json} (97%) rename core/Functions/view-function-extension/{single-task-function.json => vfe-single-task-function.json} (94%) create mode 100644 core/Functions/view-function-extension/vfe-workflow-function.json create mode 100644 core/Views/view-function-extension/deeplink-view.json create mode 100644 core/Views/view-function-extension/http-view.json create mode 100644 core/Views/view-function-extension/urn-view.json create mode 100644 tests/Core/Helpers/DomainFunctionHelper.cs create mode 100644 tests/Core/Tests/view-function-extension-test-workflow/VfeExtensionAssertions.cs create mode 100644 tests/Core/Tests/view-function-extension-test-workflow/VfeFunctionAssertions.cs create mode 100644 tests/Core/Tests/view-function-extension-test-workflow/VfeScenarioActions.cs diff --git a/api-tests/view-function-extension/postman-view-function-extension.json b/api-tests/view-function-extension/postman-view-function-extension.json index 2b443bd..d3ccb30 100644 --- a/api-tests/view-function-extension/postman-view-function-extension.json +++ b/api-tests/view-function-extension/postman-view-function-extension.json @@ -1,383 +1,558 @@ { "info": { - "_postman_id": "5422afe4-5655-4252-91ff-ecbbd776cb64", - "name": "vNext Test - View Function Extension", - "description": "Auto-generated from view-function-extension-test-workflow.http.\nvNext workflow integration test collection for view-function-extension.\nPath: view-test-state → (auto) multi-view-state → (manual) wizard-state → (manual) completed-state", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + "name": "View-Function-Extension Integration Test", + "_postman_id": "vfe-comprehensive-test", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "description": "Comprehensive integration test for view-function-extension-test-workflow.\nCovers 6 view types (JSON/HTML/Markdown/DeepLink/HTTP/URN), 6 display modes (full-page/popup/bottom-sheet/top-sheet/drawer/inline), 4 extension types (Global/GlobalAndRequested/DefinedFlows/DefinedFlowAndRequested), 3 extension scopes (GetInstance/GetAllInstances/Everywhere), 3 function scopes (Instance/Workflow/Domain), transition view rendering, and wizard state (stateType 5) behavior." }, "variable": [ - { - "key": "baseUrl", - "value": "http://localhost:4201", - "type": "string" - }, - { - "key": "apiVersion", - "value": "1", - "type": "string" - }, - { - "key": "domain", - "value": "core", - "type": "string" - }, - { - "key": "workflow", - "value": "view-function-extension-test-workflow", - "type": "string" - } + { "key": "baseUrl", "value": "http://localhost:4201" }, + { "key": "apiVersion", "value": "1" }, + { "key": "domain", "value": "core" }, + { "key": "workflow", "value": "view-function-extension-test-workflow" }, + { "key": "instanceId", "value": "" } ], "item": [ { - "name": "Step 1: Start workflow (sync)", + "name": "Step 1: Start Workflow Instance", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.collectionVariables.set('instanceId', jsonData.id);", + "pm.test('Instance started successfully', function() {", + " pm.expect(jsonData.id).to.be.a('string');", + " pm.expect(jsonData.id).to.not.be.empty;", + "});" + ], + "type": "text/javascript" + } + } + ], "request": { "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "start" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] - }, + "header": [{ "key": "Content-Type", "value": "application/json" }], "body": { "mode": "raw", - "raw": "{\n \"key\": \"vfe-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"view-function-extension\"],\n \"attributes\": {}\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n \"key\": \"vfe-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"view-function-extension\"],\n \"attributes\": {}\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"] } - }, - "response": [], + } + }, + { + "name": "Step 2: Get State (view-test-state — JSON view)", "event": [ { "listen": "test", "script": { - "type": "text/javascript", "exec": [ "var jsonData = pm.response.json();", - "if (jsonData && jsonData.id) {", - " pm.collectionVariables.set(\"vfeInstanceId\", jsonData.id);", - " console.log(\"vfeInstanceId set to: \" + jsonData.id);", - "}" - ] + "pm.test('State is view-test-state (no auto transition)', function() {", + " var state = jsonData.state || (jsonData.data && jsonData.data.state);", + " pm.expect(state).to.eql('view-test-state');", + "});" + ], + "type": "text/javascript" } } - ] + ], + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "state"] + } + } }, { - "name": "Step 2: Get state (should be multi-view-state after auto-transition)", + "name": "Step 3: Get View (view-test-state — type 1 JSON, full-page)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test('View response is OK', function() {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], "request": { "method": "GET", - "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{vfeInstanceId}}", - "functions", - "state" - ] + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/view", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "view"] + } + } + }, + { + "name": "Step 4: Instance Data (extensions scope check)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test('Functions/data response is OK', function() {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/data", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "data"] } - }, - "response": [] + } }, { - "name": "Step 3: Get view payload (HTML view on multi-view-state)", + "name": "Step 5: Data with GlobalAndRequested extension (Type 2, Scope 1)", "request": { "method": "GET", - "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/view?platform=web", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{vfeInstanceId}}", - "functions", - "view" - ], + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/data?extensions=vfe-global-and-requested-extension", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "data"], "query": [ - { - "key": "platform", - "value": "web" - } + { "key": "extensions", "value": "vfe-global-and-requested-extension" } ] } - }, - "response": [] + } }, { - "name": "Step 4: Execute single-task function (instance-scoped, script task)", + "name": "Step 6: Data with DefinedFlowAndRequested extension (Type 4, Scope 3)", "request": { "method": "GET", - "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/single-task-function", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{vfeInstanceId}}", - "functions", - "single-task-function" + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/data?extensions=vfe-defined-flow-and-requested-extension", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "data"], + "query": [ + { "key": "extensions", "value": "vfe-defined-flow-and-requested-extension" } ] } - }, - "response": [] + } }, { - "name": "Step 5: Execute multi-task function (two script tasks; output handler aggregates)", + "name": "Step 7: List Instances (Scope 2 GetAllInstances enrichment)", "request": { "method": "GET", - "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/multi-task-function", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{vfeInstanceId}}", - "functions", - "multi-task-function" + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances?pageSize=10", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances"], + "query": [ + { "key": "pageSize", "value": "10" } ] } - }, - "response": [] + } }, { - "name": "Step 6: Instance data without extension filter", + "name": "Step 8: Single-Task Function (scope I)", "request": { "method": "GET", - "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/data", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{vfeInstanceId}}", - "functions", - "data" - ] + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/vfe-single-task-function", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "vfe-single-task-function"] } - }, - "response": [] + } }, { - "name": "Step 7: Requested extensions via query", + "name": "Step 9: Multi-Task Function (scope I)", "request": { "method": "GET", - "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/data?extensions=requested-extension,global-extension", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{vfeInstanceId}}", - "functions", - "data" - ], - "query": [ - { - "key": "extensions", - "value": "requested-extension,global-extension" - } - ] + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/vfe-multi-task-function", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "vfe-multi-task-function"] + } + } + }, + { + "name": "Step 10: Workflow-Scope Function (scope F) — may return 404", + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/functions/vfe-workflow-function", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "functions", "vfe-workflow-function"] } - }, - "response": [] + } }, { - "name": "Step 8: Transition to wizard-state", + "name": "Step 11: Domain-Scope Function (scope D) — may return empty", + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/functions/vfe-domain-function", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "functions", "vfe-domain-function"] + } + } + }, + { + "name": "Step 12: Transition continue-to-html (transition view: json-view)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test('Transition accepted', function() {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], "request": { "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" + "header": [{ "key": "Content-Type", "value": "application/json" }], + "body": { "mode": "raw", "raw": "{}" }, + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/continue-to-html", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "transitions", "continue-to-html"] + } + } + }, + { + "name": "Step 13: Get State (html-view-state)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.test('State is html-view-state', function() {", + " var state = jsonData.state || (jsonData.data && jsonData.data.state);", + " pm.expect(state).to.eql('html-view-state');", + "});" + ], + "type": "text/javascript" } - ], + } + ], + "request": { + "method": "GET", "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/transitions/manual-to-wizard", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{vfeInstanceId}}", - "transitions", - "manual-to-wizard" - ] - }, - "body": { - "mode": "raw", - "raw": "{}", - "options": { - "raw": { - "language": "json" - } + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "state"] + } + } + }, + { + "name": "Step 14: Get View (html — type 2, popup)", + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/view", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "view"] + } + } + }, + { + "name": "Step 15: Transition to markdown-view-state", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json" }], + "body": { "mode": "raw", "raw": "{}" }, + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/to-markdown", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "transitions", "to-markdown"] + } + } + }, + { + "name": "Step 16: Get State (markdown-view-state)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.test('State is markdown-view-state', function() {", + " var state = jsonData.state || (jsonData.data && jsonData.data.state);", + " pm.expect(state).to.eql('markdown-view-state');", + "});" + ], + "type": "text/javascript" } } - }, - "response": [] + ], + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "state"] + } + } }, { - "name": "Step 9: Verify wizard-state (stateType 5, at most 1 transition)", + "name": "Step 17: Get View (markdown — type 3, bottom-sheet)", "request": { "method": "GET", - "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{vfeInstanceId}}", - "functions", - "state" - ] + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/view", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "view"] } - }, - "response": [] + } }, { - "name": "Step 10: Complete workflow (complete-with-markdown-view)", + "name": "Step 18: Transition to deeplink-view-state", "request": { "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" + "header": [{ "key": "Content-Type", "value": "application/json" }], + "body": { "mode": "raw", "raw": "{}" }, + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/to-deeplink", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "transitions", "to-deeplink"] + } + } + }, + { + "name": "Step 19: Get State (deeplink-view-state)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.test('State is deeplink-view-state', function() {", + " var state = jsonData.state || (jsonData.data && jsonData.data.state);", + " pm.expect(state).to.eql('deeplink-view-state');", + "});" + ], + "type": "text/javascript" } - ], + } + ], + "request": { + "method": "GET", "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/transitions/complete-with-markdown-view", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{vfeInstanceId}}", - "transitions", - "complete-with-markdown-view" - ] - }, - "body": { - "mode": "raw", - "raw": "{}", - "options": { - "raw": { - "language": "json" - } + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "state"] + } + } + }, + { + "name": "Step 20: Get View (deeplink — type 4, top-sheet)", + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/view", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "view"] + } + } + }, + { + "name": "Step 21: Transition to http-view-state", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json" }], + "body": { "mode": "raw", "raw": "{}" }, + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/to-http", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "transitions", "to-http"] + } + } + }, + { + "name": "Step 22: Get State (http-view-state)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.test('State is http-view-state', function() {", + " var state = jsonData.state || (jsonData.data && jsonData.data.state);", + " pm.expect(state).to.eql('http-view-state');", + "});" + ], + "type": "text/javascript" } } - }, - "response": [] + ], + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "state"] + } + } }, { - "name": "Step 11: Verify completed-state with status C", + "name": "Step 23: Get View (http — type 5, drawer)", "request": { "method": "GET", - "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{vfeInstanceId}}", - "functions", - "state" - ] + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/view", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "view"] + } + } + }, + { + "name": "Step 24: Transition to urn-view-state", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json" }], + "body": { "mode": "raw", "raw": "{}" }, + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/to-urn", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "transitions", "to-urn"] + } + } + }, + { + "name": "Step 25: Get State (urn-view-state — intermediate, type 6 inline)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.test('State is urn-view-state', function() {", + " var state = jsonData.state || (jsonData.data && jsonData.data.state);", + " pm.expect(state).to.eql('urn-view-state');", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "state"] + } + } + }, + { + "name": "Step 26: Get View (urn — type 6, inline) — may return 404", + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/view", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "view"] + } + } + }, + { + "name": "Step 27: Transition to wizard-step-state (stateType 5)", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json" }], + "body": { "mode": "raw", "raw": "{}" }, + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/to-wizard", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "transitions", "to-wizard"] + } + } + }, + { + "name": "Step 28: Get State (wizard-step-state — stateType 5, transition view)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.test('State is wizard-step-state', function() {", + " var state = jsonData.state || (jsonData.data && jsonData.data.state);", + " pm.expect(state).to.eql('wizard-step-state');", + "});", + "pm.test('Wizard state has exactly 1 transition', function() {", + " var transitions = jsonData.transitions || (jsonData.data && jsonData.data.transitions);", + " pm.expect(transitions).to.be.an('array').with.lengthOf(1);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "state"] + } + } + }, + { + "name": "Step 29: Complete Workflow", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json" }], + "body": { "mode": "raw", "raw": "{}" }, + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/complete", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "transitions", "complete"] + } + } + }, + { + "name": "Step 30: Get Final State (completed-state, status: C)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = pm.response.json();", + "pm.test('Instance is completed', function() {", + " var status = jsonData.status || (jsonData.data && jsonData.data.status);", + " pm.expect(status).to.eql('C');", + "});", + "pm.test('State is completed-state', function() {", + " var state = jsonData.state || (jsonData.data && jsonData.data.state);", + " pm.expect(state).to.eql('completed-state');", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{instanceId}}", "functions", "state"] } - }, - "response": [] + } } ] } diff --git a/api-tests/view-function-extension/view-function-extension-test-workflow.http b/api-tests/view-function-extension/view-function-extension-test-workflow.http index 2c168c5..0cb8b88 100644 --- a/api-tests/view-function-extension/view-function-extension-test-workflow.http +++ b/api-tests/view-function-extension/view-function-extension-test-workflow.http @@ -3,15 +3,9 @@ @domain = core @workflow = view-function-extension-test-workflow -############################################################################### -# View, function, and extension integration tests. -# Workflow path: view-test-state → (auto) multi-view-state → (manual) -# wizard-state → (manual) completed-state -############################################################################### - -### Step 1: Start workflow (sync) — lands on view-test-state, auto-transitions to multi-view-state -# @name createVfeInstance -POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +### Step 1: Start Workflow Instance +# @name createInstance +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start Content-Type: application/json { @@ -20,40 +14,122 @@ Content-Type: application/json "attributes": {} } -@vfeInstanceId = {{createVfeInstance.response.body.$.id}} +@instanceId = {{createInstance.response.body.$.id}} + +### Step 2: Get State (view-test-state — JSON view, stateType 1) +# Instance stays in view-test-state (no auto transition) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state + +### Step 3: Get View (view-test-state — type 1 JSON, display full-page) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/view + +### Step 4: Instance Data (extensions scope check) +# Type 1 Global (Scope 3 Everywhere): implicit, NOT in workflow extensions — should auto-appear +# Type 2 GlobalAndRequested (Scope 1 GetInstance): appears in default or with ?extensions= query +# Type 3 DefinedFlows (Scope 2 GetAllInstances): appears in list endpoint enrichment +# Type 4 DefinedFlowAndRequested (Scope 3 Everywhere): appears only with ?extensions= query +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/data + +### Step 5: Data with GlobalAndRequested extension (Type 2, Scope 1) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/data?extensions=vfe-global-and-requested-extension + +### Step 6: Data with DefinedFlowAndRequested extension (Type 4, Scope 3) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/data?extensions=vfe-defined-flow-and-requested-extension + +### Step 7: List Instances (Scope 2 GetAllInstances enrichment test) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances?pageSize=10 + +### Step 8: Single-Task Function (scope I — Instance) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/vfe-single-task-function + +### Step 9: Multi-Task Function (scope I — Instance) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/vfe-multi-task-function -### Step 2: Get state — should be multi-view-state (auto-transition fires immediately) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/state +### Step 10: Workflow-Scope Function (scope F) +# NOTE: Runtime may return 404 for workflow-scope functions — known limitation +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/functions/vfe-workflow-function -### Step 3: Get view payload (state-level HTML view on multi-view-state, optional platform filter) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/view?platform=web +### Step 11: Domain-Scope Function (scope D) +# NOTE: Runtime may return empty body for domain-scope functions — known limitation +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/functions/vfe-domain-function -### Step 4: Execute single-task function (instance-scoped, script task) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/single-task-function +### Step 12: Transition continue-to-html (manual, transition view: json-view) +# This transition has a view reference attached — tests transition view rendering +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/continue-to-html +Content-Type: application/json + +{} + +### Step 13: Get State (html-view-state — type 2, popup) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state + +### Step 14: Get View (html-view-state — type 2, display popup) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/view + +### Step 15: Transition to markdown-view-state +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/to-markdown +Content-Type: application/json + +{} -### Step 5: Execute multi-task function (two script tasks; output handler aggregates) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/multi-task-function +### Step 16: Get State (markdown-view-state) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state + +### Step 17: Get View (markdown-view-state — type 3, display bottom-sheet) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/view + +### Step 18: Transition to deeplink-view-state +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/to-deeplink +Content-Type: application/json + +{} + +### Step 19: Get State (deeplink-view-state) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state + +### Step 20: Get View (deeplink-view-state — type 4, display top-sheet) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/view + +### Step 21: Transition to http-view-state +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/to-http +Content-Type: application/json + +{} + +### Step 22: Get State (http-view-state) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state + +### Step 23: Get View (http-view-state — type 5, display drawer) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/view + +### Step 24: Transition to urn-view-state +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/to-urn +Content-Type: application/json + +{} -### Step 6: Instance data without extension filter (global-extension scope Everywhere may apply) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/data +### Step 25: Get State (urn-view-state — type 6, inline) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state -### Step 7: Requested extensions via query (DefinedFlowAndRequested + on-demand) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/data?extensions=requested-extension,global-extension +### Step 26: Get View (urn-view-state — type 6, inline) +# NOTE: Runtime may return 404 for URN view type — known limitation +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/view -### Step 8: Transition to wizard-state (manual-to-wizard) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/transitions/manual-to-wizard +### Step 27: Transition to wizard-step-state (stateType 5) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/to-wizard Content-Type: application/json {} -### Step 9: Verify wizard-state — stateType 5, at most 1 transition, transition has markdown-view -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/state +### Step 28: Get State (wizard-step-state — stateType 5, transition view rendered) +# Wizard state: exactly 1 transition, no state view, transition view rendered +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state -### Step 10: Complete workflow (complete-with-markdown-view transition) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/transitions/complete-with-markdown-view +### Step 29: Complete Workflow +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/transitions/complete Content-Type: application/json {} -### Step 11: Verify completed-state with status C -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{vfeInstanceId}}/functions/state +### Step 30: Get Final State (completed-state, status: C) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceId}}/functions/state diff --git a/core.link.json b/core.link.json index 8327ba3..46acb9a 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-05-05T11:18:04.858Z" + "importedAt": "2026-05-06T06:49:44.323Z" } \ No newline at end of file diff --git a/core/Extensions/view-function-extension/global-extension.json b/core/Extensions/view-function-extension/global-extension.json deleted file mode 100644 index c5627c9..0000000 --- a/core/Extensions/view-function-extension/global-extension.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "key": "global-extension", - "version": "1.0.0", - "domain": "core", - "flow": "sys-extensions", - "flowVersion": "1.0.0", - "tags": [ - "integration-test", - "view-function-extension", - "global-extension" - ], - "attributes": { - "type": 1, - "scope": 3, - "task": { - "order": 1, - "task": { - "key": "vfe-script-task", - "domain": "core", - "version": "1.0.0", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/GlobalExtensionMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBHbG9iYWxFeHRlbnNpb25NYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LmV4dGVuc2lvblR5cGUgPSAiZ2xvYmFsIjsKICAgICAgICByZXN1bHQudGltZXN0YW1wID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" - } - } - } -} diff --git a/core/Extensions/view-function-extension/requested-extension.json b/core/Extensions/view-function-extension/requested-extension.json deleted file mode 100644 index dfed388..0000000 --- a/core/Extensions/view-function-extension/requested-extension.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "key": "requested-extension", - "version": "1.0.0", - "domain": "core", - "flow": "sys-extensions", - "flowVersion": "1.0.0", - "tags": [ - "integration-test", - "view-function-extension", - "requested-extension" - ], - "attributes": { - "type": 4, - "scope": 1, - "task": { - "order": 1, - "task": { - "key": "vfe-script-task", - "domain": "core", - "version": "1.0.0", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/RequestedExtensionMapping.csx", - "code": "dXNpbmcgU3lzdGVtLkR5bmFtaWM7CnVzaW5nIFN5c3RlbS5UaHJlYWRpbmcuVGFza3M7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CgpwdWJsaWMgY2xhc3MgUmVxdWVzdGVkRXh0ZW5zaW9uTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC5leHRlbnNpb25UeXBlID0gInJlcXVlc3RlZCI7CiAgICAgICAgcmVzdWx0Lm9uRGVtYW5kID0gdHJ1ZTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" - } - } - } -} diff --git a/core/Extensions/view-function-extension/src/VfeDefinedFlowAndRequestedExtensionMapping.csx b/core/Extensions/view-function-extension/src/VfeDefinedFlowAndRequestedExtensionMapping.csx new file mode 100644 index 0000000..a66fc86 --- /dev/null +++ b/core/Extensions/view-function-extension/src/VfeDefinedFlowAndRequestedExtensionMapping.csx @@ -0,0 +1,21 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; + +public class VfeDefinedFlowAndRequestedExtensionMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public Task OutputHandler(ScriptContext context) + { + dynamic result = new ExpandoObject(); + result.vfeExtensionType = "definedFlowAndRequested"; + result.vfeExtensionScope = "getInstance"; + result.appliedAt = DateTime.UtcNow.ToString("o"); + return Task.FromResult(new ScriptResponse { Data = result }); + } +} diff --git a/core/Extensions/view-function-extension/src/VfeDefinedFlowsExtensionMapping.csx b/core/Extensions/view-function-extension/src/VfeDefinedFlowsExtensionMapping.csx new file mode 100644 index 0000000..1b06bcc --- /dev/null +++ b/core/Extensions/view-function-extension/src/VfeDefinedFlowsExtensionMapping.csx @@ -0,0 +1,21 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; + +public class VfeDefinedFlowsExtensionMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public Task OutputHandler(ScriptContext context) + { + dynamic result = new ExpandoObject(); + result.vfeExtensionType = "definedFlows"; + result.vfeExtensionScope = "getAllInstances"; + result.appliedAt = DateTime.UtcNow.ToString("o"); + return Task.FromResult(new ScriptResponse { Data = result }); + } +} diff --git a/core/Extensions/view-function-extension/src/VfeGlobalAndRequestedExtensionMapping.csx b/core/Extensions/view-function-extension/src/VfeGlobalAndRequestedExtensionMapping.csx new file mode 100644 index 0000000..5dbae2d --- /dev/null +++ b/core/Extensions/view-function-extension/src/VfeGlobalAndRequestedExtensionMapping.csx @@ -0,0 +1,21 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; + +public class VfeGlobalAndRequestedExtensionMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public Task OutputHandler(ScriptContext context) + { + dynamic result = new ExpandoObject(); + result.vfeExtensionType = "globalAndRequested"; + result.vfeExtensionScope = "getInstance"; + result.appliedAt = DateTime.UtcNow.ToString("o"); + return Task.FromResult(new ScriptResponse { Data = result }); + } +} diff --git a/core/Extensions/view-function-extension/src/VfeGlobalExtensionMapping.csx b/core/Extensions/view-function-extension/src/VfeGlobalExtensionMapping.csx new file mode 100644 index 0000000..69148a2 --- /dev/null +++ b/core/Extensions/view-function-extension/src/VfeGlobalExtensionMapping.csx @@ -0,0 +1,21 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; + +public class VfeGlobalExtensionMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public Task OutputHandler(ScriptContext context) + { + dynamic result = new ExpandoObject(); + result.vfeExtensionType = "global"; + result.vfeExtensionScope = "everywhere"; + result.appliedAt = DateTime.UtcNow.ToString("o"); + return Task.FromResult(new ScriptResponse { Data = result }); + } +} diff --git a/core/Extensions/view-function-extension/vfe-defined-flow-and-requested-extension.json b/core/Extensions/view-function-extension/vfe-defined-flow-and-requested-extension.json new file mode 100644 index 0000000..b37f09a --- /dev/null +++ b/core/Extensions/view-function-extension/vfe-defined-flow-and-requested-extension.json @@ -0,0 +1,31 @@ +{ + "key": "vfe-defined-flow-and-requested-extension", + "version": "1.0.0", + "domain": "core", + "flow": "sys-extensions", + "flowVersion": "1.0.0", + "tags": [ + "integration-test", + "view-function-extension", + "defined-flow-and-requested", + "type-4", + "scope-1" + ], + "attributes": { + "type": 4, + "scope": 1, + "task": { + "order": 1, + "task": { + "key": "vfe-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/VfeDefinedFlowAndRequestedExtensionMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBWZmVEZWZpbmVkRmxvd0FuZFJlcXVlc3RlZEV4dGVuc2lvbk1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQudmZlRXh0ZW5zaW9uVHlwZSA9ICJkZWZpbmVkRmxvd0FuZFJlcXVlc3RlZCI7CiAgICAgICAgcmVzdWx0LnZmZUV4dGVuc2lvblNjb3BlID0gImdldEluc3RhbmNlIjsKICAgICAgICByZXN1bHQuYXBwbGllZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" + } + } + } +} \ No newline at end of file diff --git a/core/Extensions/view-function-extension/vfe-defined-flows-extension.json b/core/Extensions/view-function-extension/vfe-defined-flows-extension.json new file mode 100644 index 0000000..52286af --- /dev/null +++ b/core/Extensions/view-function-extension/vfe-defined-flows-extension.json @@ -0,0 +1,31 @@ +{ + "key": "vfe-defined-flows-extension", + "version": "1.0.0", + "domain": "core", + "flow": "sys-extensions", + "flowVersion": "1.0.0", + "tags": [ + "integration-test", + "view-function-extension", + "defined-flows", + "type-3", + "scope-2" + ], + "attributes": { + "type": 3, + "scope": 2, + "task": { + "order": 1, + "task": { + "key": "vfe-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/VfeDefinedFlowsExtensionMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBWZmVEZWZpbmVkRmxvd3NFeHRlbnNpb25NYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnZmZUV4dGVuc2lvblR5cGUgPSAiZGVmaW5lZEZsb3dzIjsKICAgICAgICByZXN1bHQudmZlRXh0ZW5zaW9uU2NvcGUgPSAiZ2V0QWxsSW5zdGFuY2VzIjsKICAgICAgICByZXN1bHQuYXBwbGllZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" + } + } + } +} \ No newline at end of file diff --git a/core/Extensions/view-function-extension/vfe-global-and-requested-extension.json b/core/Extensions/view-function-extension/vfe-global-and-requested-extension.json new file mode 100644 index 0000000..35322e3 --- /dev/null +++ b/core/Extensions/view-function-extension/vfe-global-and-requested-extension.json @@ -0,0 +1,31 @@ +{ + "key": "vfe-global-and-requested-extension", + "version": "1.0.0", + "domain": "core", + "flow": "sys-extensions", + "flowVersion": "1.0.0", + "tags": [ + "integration-test", + "view-function-extension", + "global-and-requested", + "type-2", + "scope-1" + ], + "attributes": { + "type": 2, + "scope": 1, + "task": { + "order": 1, + "task": { + "key": "vfe-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/VfeGlobalAndRequestedExtensionMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBWZmVHbG9iYWxBbmRSZXF1ZXN0ZWRFeHRlbnNpb25NYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnZmZUV4dGVuc2lvblR5cGUgPSAiZ2xvYmFsQW5kUmVxdWVzdGVkIjsKICAgICAgICByZXN1bHQudmZlRXh0ZW5zaW9uU2NvcGUgPSAiZ2V0SW5zdGFuY2UiOwogICAgICAgIHJlc3VsdC5hcHBsaWVkQXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" + } + } + } +} \ No newline at end of file diff --git a/core/Extensions/view-function-extension/vfe-global-extension.json b/core/Extensions/view-function-extension/vfe-global-extension.json new file mode 100644 index 0000000..bc06ef6 --- /dev/null +++ b/core/Extensions/view-function-extension/vfe-global-extension.json @@ -0,0 +1,31 @@ +{ + "key": "vfe-global-extension", + "version": "1.0.0", + "domain": "core", + "flow": "sys-extensions", + "flowVersion": "1.0.0", + "tags": [ + "integration-test", + "view-function-extension", + "global-extension", + "type-1", + "scope-3" + ], + "attributes": { + "type": 1, + "scope": 3, + "task": { + "order": 1, + "task": { + "key": "vfe-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/VfeGlobalExtensionMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBWZmVHbG9iYWxFeHRlbnNpb25NYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgcmVzdWx0LnZmZUV4dGVuc2lvblR5cGUgPSAiZ2xvYmFsIjsKICAgICAgICByZXN1bHQudmZlRXh0ZW5zaW9uU2NvcGUgPSAiZXZlcnl3aGVyZSI7CiAgICAgICAgcmVzdWx0LmFwcGxpZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" + } + } + } +} \ No newline at end of file diff --git a/core/Extensions/view-function-extension/src/GlobalExtensionMapping.csx b/core/Functions/view-function-extension/src/VfeDomainFunctionMapping.csx similarity index 69% rename from core/Extensions/view-function-extension/src/GlobalExtensionMapping.csx rename to core/Functions/view-function-extension/src/VfeDomainFunctionMapping.csx index 36ff29d..107296c 100644 --- a/core/Extensions/view-function-extension/src/GlobalExtensionMapping.csx +++ b/core/Functions/view-function-extension/src/VfeDomainFunctionMapping.csx @@ -3,7 +3,7 @@ using System.Dynamic; using System.Threading.Tasks; using BBT.Workflow.Scripting; -public class GlobalExtensionMapping : ScriptBase, IMapping +public class VfeDomainFunctionMapping : ScriptBase, IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) { @@ -13,8 +13,9 @@ public class GlobalExtensionMapping : ScriptBase, IMapping public Task OutputHandler(ScriptContext context) { dynamic result = new ExpandoObject(); - result.extensionType = "global"; - result.timestamp = DateTime.UtcNow.ToString("o"); + result.functionScope = "D"; + result.domainFunction = true; + result.executedAt = DateTime.UtcNow.ToString("o"); return Task.FromResult(new ScriptResponse { Data = result }); } } diff --git a/core/Extensions/view-function-extension/src/RequestedExtensionMapping.csx b/core/Functions/view-function-extension/src/VfeWorkflowFunctionMapping.csx similarity index 67% rename from core/Extensions/view-function-extension/src/RequestedExtensionMapping.csx rename to core/Functions/view-function-extension/src/VfeWorkflowFunctionMapping.csx index 8315ec2..2641ae1 100644 --- a/core/Extensions/view-function-extension/src/RequestedExtensionMapping.csx +++ b/core/Functions/view-function-extension/src/VfeWorkflowFunctionMapping.csx @@ -1,8 +1,9 @@ +using System; using System.Dynamic; using System.Threading.Tasks; using BBT.Workflow.Scripting; -public class RequestedExtensionMapping : ScriptBase, IMapping +public class VfeWorkflowFunctionMapping : ScriptBase, IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) { @@ -12,8 +13,9 @@ public class RequestedExtensionMapping : ScriptBase, IMapping public Task OutputHandler(ScriptContext context) { dynamic result = new ExpandoObject(); - result.extensionType = "requested"; - result.onDemand = true; + result.functionScope = "F"; + result.workflowFunction = true; + result.executedAt = DateTime.UtcNow.ToString("o"); return Task.FromResult(new ScriptResponse { Data = result }); } } diff --git a/core/Functions/view-function-extension/vfe-domain-function.json b/core/Functions/view-function-extension/vfe-domain-function.json new file mode 100644 index 0000000..f30bdd1 --- /dev/null +++ b/core/Functions/view-function-extension/vfe-domain-function.json @@ -0,0 +1,29 @@ +{ + "key": "vfe-domain-function", + "version": "1.0.0", + "domain": "core", + "flow": "sys-functions", + "flowVersion": "1.0.0", + "tags": [ + "integration-test", + "view-function-extension", + "domain-function", + "scope-D" + ], + "attributes": { + "scope": "D", + "task": { + "order": 1, + "task": { + "key": "vfe-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/VfeDomainFunctionMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBWZmVEb21haW5GdW5jdGlvbk1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICByZXN1bHQuZnVuY3Rpb25TY29wZSA9ICJEIjsKICAgICAgICByZXN1bHQuZG9tYWluRnVuY3Rpb24gPSB0cnVlOwogICAgICAgIHJlc3VsdC5leGVjdXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" + } + } + } +} \ No newline at end of file diff --git a/core/Functions/view-function-extension/multi-task-function.json b/core/Functions/view-function-extension/vfe-multi-task-function.json similarity index 97% rename from core/Functions/view-function-extension/multi-task-function.json rename to core/Functions/view-function-extension/vfe-multi-task-function.json index 9b6682d..b092cca 100644 --- a/core/Functions/view-function-extension/multi-task-function.json +++ b/core/Functions/view-function-extension/vfe-multi-task-function.json @@ -1,5 +1,5 @@ { - "key": "multi-task-function", + "key": "vfe-multi-task-function", "version": "1.0.0", "domain": "core", "flow": "sys-functions", @@ -7,7 +7,8 @@ "tags": [ "integration-test", "view-function-extension", - "multi-task-function" + "multi-task-function", + "scope-I" ], "attributes": { "scope": "I", diff --git a/core/Functions/view-function-extension/single-task-function.json b/core/Functions/view-function-extension/vfe-single-task-function.json similarity index 94% rename from core/Functions/view-function-extension/single-task-function.json rename to core/Functions/view-function-extension/vfe-single-task-function.json index 54f73e5..982475e 100644 --- a/core/Functions/view-function-extension/single-task-function.json +++ b/core/Functions/view-function-extension/vfe-single-task-function.json @@ -1,5 +1,5 @@ { - "key": "single-task-function", + "key": "vfe-single-task-function", "version": "1.0.0", "domain": "core", "flow": "sys-functions", @@ -7,7 +7,8 @@ "tags": [ "integration-test", "view-function-extension", - "single-task-function" + "single-task-function", + "scope-I" ], "attributes": { "scope": "I", @@ -25,4 +26,4 @@ } } } -} +} \ No newline at end of file diff --git a/core/Functions/view-function-extension/vfe-workflow-function.json b/core/Functions/view-function-extension/vfe-workflow-function.json new file mode 100644 index 0000000..c310963 --- /dev/null +++ b/core/Functions/view-function-extension/vfe-workflow-function.json @@ -0,0 +1,29 @@ +{ + "key": "vfe-workflow-function", + "version": "1.0.0", + "domain": "core", + "flow": "sys-functions", + "flowVersion": "1.0.0", + "tags": [ + "integration-test", + "view-function-extension", + "workflow-function", + "scope-F" + ], + "attributes": { + "scope": "F", + "task": { + "order": 1, + "task": { + "key": "vfe-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/VfeWorkflowFunctionMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBWZmVXb3JrZmxvd0Z1bmN0aW9uTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC5mdW5jdGlvblNjb3BlID0gIkYiOwogICAgICAgIHJlc3VsdC53b3JrZmxvd0Z1bmN0aW9uID0gdHJ1ZTsKICAgICAgICByZXN1bHQuZXhlY3V0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" + } + } + } +} \ No newline at end of file diff --git a/core/Views/view-function-extension/deeplink-view.json b/core/Views/view-function-extension/deeplink-view.json new file mode 100644 index 0000000..0310c31 --- /dev/null +++ b/core/Views/view-function-extension/deeplink-view.json @@ -0,0 +1,17 @@ +{ + "key": "deeplink-view", + "version": "1.0.0", + "domain": "core", + "flow": "sys-views", + "flowVersion": "1.0.0", + "tags": ["integration-test", "view-function-extension", "deeplink-view"], + "attributes": { + "type": 4, + "display": "top-sheet", + "labels": [ + { "language": "en-US", "label": "Deeplink View" }, + { "language": "tr-TR", "label": "Deeplink Görünüm" } + ], + "content": "{\"href\":\"on-burgan://vfe/test\"}" + } +} diff --git a/core/Views/view-function-extension/html-view.json b/core/Views/view-function-extension/html-view.json index 2feb09a..50a458b 100644 --- a/core/Views/view-function-extension/html-view.json +++ b/core/Views/view-function-extension/html-view.json @@ -12,6 +12,10 @@ "attributes": { "type": 2, "display": "popup", + "labels": [ + { "language": "en-US", "label": "HTML View" }, + { "language": "tr-TR", "label": "HTML Görünüm" } + ], "content": "

HTML View Test

Integration test view

" } } diff --git a/core/Views/view-function-extension/http-view.json b/core/Views/view-function-extension/http-view.json new file mode 100644 index 0000000..6d3ecbe --- /dev/null +++ b/core/Views/view-function-extension/http-view.json @@ -0,0 +1,17 @@ +{ + "key": "http-view", + "version": "1.0.0", + "domain": "core", + "flow": "sys-views", + "flowVersion": "1.0.0", + "tags": ["integration-test", "view-function-extension", "http-view"], + "attributes": { + "type": 5, + "display": "drawer", + "labels": [ + { "language": "en-US", "label": "HTTP View" }, + { "language": "tr-TR", "label": "HTTP Görünüm" } + ], + "content": "{\"href\":\"https://example.com/vfe/test\"}" + } +} diff --git a/core/Views/view-function-extension/json-view.json b/core/Views/view-function-extension/json-view.json index 7deb215..34a130d 100644 --- a/core/Views/view-function-extension/json-view.json +++ b/core/Views/view-function-extension/json-view.json @@ -12,6 +12,10 @@ "attributes": { "type": 1, "display": "full-page", + "labels": [ + { "language": "en-US", "label": "JSON View" }, + { "language": "tr-TR", "label": "JSON Görünüm" } + ], "content": "{\"title\":\"JSON View Test\",\"fields\":[{\"key\":\"status\",\"label\":\"Status\"}]}" } } diff --git a/core/Views/view-function-extension/markdown-view.json b/core/Views/view-function-extension/markdown-view.json index 4cb62ca..173292c 100644 --- a/core/Views/view-function-extension/markdown-view.json +++ b/core/Views/view-function-extension/markdown-view.json @@ -12,6 +12,10 @@ "attributes": { "type": 3, "display": "bottom-sheet", + "labels": [ + { "language": "en-US", "label": "Markdown View" }, + { "language": "tr-TR", "label": "Markdown Görünüm" } + ], "content": "# Markdown View\n\nIntegration test view" } } diff --git a/core/Views/view-function-extension/urn-view.json b/core/Views/view-function-extension/urn-view.json new file mode 100644 index 0000000..6b0adba --- /dev/null +++ b/core/Views/view-function-extension/urn-view.json @@ -0,0 +1,17 @@ +{ + "key": "urn-view", + "version": "1.0.0", + "domain": "core", + "flow": "sys-views", + "flowVersion": "1.0.0", + "tags": ["integration-test", "view-function-extension", "urn-view"], + "attributes": { + "type": 6, + "display": "inline", + "labels": [ + { "language": "en-US", "label": "URN View" }, + { "language": "tr-TR", "label": "URN Görünüm" } + ], + "content": "{\"urn\":\"urn:vnext:flow:continue:core:view-function-extension-test-workflow\"}" + } +} diff --git a/core/Workflows/error-boundary/error-boundary-test-workflow.json b/core/Workflows/error-boundary/error-boundary-test-workflow.json index ceedebe..b6b38d2 100644 --- a/core/Workflows/error-boundary/error-boundary-test-workflow.json +++ b/core/Workflows/error-boundary/error-boundary-test-workflow.json @@ -462,4 +462,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/flow-types/core-flow-test.json b/core/Workflows/flow-types/core-flow-test.json index ebe8f94..b1bf636 100644 --- a/core/Workflows/flow-types/core-flow-test.json +++ b/core/Workflows/flow-types/core-flow-test.json @@ -119,4 +119,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/flow-types/flow-flow-test.json b/core/Workflows/flow-types/flow-flow-test.json index f0b899c..d65a21f 100644 --- a/core/Workflows/flow-types/flow-flow-test.json +++ b/core/Workflows/flow-types/flow-flow-test.json @@ -119,4 +119,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/flow-types/subflow-flow-test.json b/core/Workflows/flow-types/subflow-flow-test.json index 542baa4..5740a6d 100644 --- a/core/Workflows/flow-types/subflow-flow-test.json +++ b/core/Workflows/flow-types/subflow-flow-test.json @@ -119,4 +119,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/flow-types/subprocess-flow-test.json b/core/Workflows/flow-types/subprocess-flow-test.json index ab1e063..2bb0998 100644 --- a/core/Workflows/flow-types/subprocess-flow-test.json +++ b/core/Workflows/flow-types/subprocess-flow-test.json @@ -119,4 +119,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/instance-management/instance-management-test-workflow.json b/core/Workflows/instance-management/instance-management-test-workflow.json index 3802507..4529799 100644 --- a/core/Workflows/instance-management/instance-management-test-workflow.json +++ b/core/Workflows/instance-management/instance-management-test-workflow.json @@ -318,4 +318,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json index b8bfa54..6b54861 100644 --- a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json +++ b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json @@ -501,4 +501,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/payments/payment-process.json b/core/Workflows/payments/payment-process.json index 11c65ba..a5c89ce 100644 --- a/core/Workflows/payments/payment-process.json +++ b/core/Workflows/payments/payment-process.json @@ -348,4 +348,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/payments/scheduled-payments-workflow.json b/core/Workflows/payments/scheduled-payments-workflow.json index c493218..02c0729 100644 --- a/core/Workflows/payments/scheduled-payments-workflow.json +++ b/core/Workflows/payments/scheduled-payments-workflow.json @@ -401,4 +401,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/schema-data/schema-data-test-workflow.json b/core/Workflows/schema-data/schema-data-test-workflow.json index 814413f..7297e47 100644 --- a/core/Workflows/schema-data/schema-data-test-workflow.json +++ b/core/Workflows/schema-data/schema-data-test-workflow.json @@ -227,4 +227,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/subflow-orchestration/subflow-orchestration-child.json b/core/Workflows/subflow-orchestration/subflow-orchestration-child.json index 4808e42..c4221fb 100644 --- a/core/Workflows/subflow-orchestration/subflow-orchestration-child.json +++ b/core/Workflows/subflow-orchestration/subflow-orchestration-child.json @@ -239,4 +239,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/subflow-orchestration/subflow-orchestration-grandchild.json b/core/Workflows/subflow-orchestration/subflow-orchestration-grandchild.json index 7f63964..edfc1b8 100644 --- a/core/Workflows/subflow-orchestration/subflow-orchestration-grandchild.json +++ b/core/Workflows/subflow-orchestration/subflow-orchestration-grandchild.json @@ -143,4 +143,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json b/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json index c0dd84c..66e38c0 100644 --- a/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json +++ b/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json @@ -167,16 +167,28 @@ "states": { "child-initial": { "queryRoles": [ - { "role": "morph-idm.maker", "grant": "allow" }, - { "role": "morph-idm.viewer", "grant": "deny" } + { + "role": "morph-idm.maker", + "grant": "allow" + }, + { + "role": "morph-idm.viewer", + "grant": "deny" + } ] } }, "transitions": { "auto-child-to-subflow": { "roles": [ - { "role": "morph-idm.maker", "grant": "allow" }, - { "role": "morph-idm.viewer", "grant": "deny" } + { + "role": "morph-idm.maker", + "grant": "allow" + }, + { + "role": "morph-idm.viewer", + "grant": "deny" + } ] } } @@ -240,4 +252,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/task-execution/task-execution-test-workflow.json b/core/Workflows/task-execution/task-execution-test-workflow.json index f1d6de5..1a74ce1 100644 --- a/core/Workflows/task-execution/task-execution-test-workflow.json +++ b/core/Workflows/task-execution/task-execution-test-workflow.json @@ -518,4 +518,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/version-consistency/version-consistency-test-workflow-v2.json b/core/Workflows/version-consistency/version-consistency-test-workflow-v2.json index 73e42ec..25ab41e 100644 --- a/core/Workflows/version-consistency/version-consistency-test-workflow-v2.json +++ b/core/Workflows/version-consistency/version-consistency-test-workflow-v2.json @@ -195,4 +195,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/version-consistency/version-consistency-test-workflow.json b/core/Workflows/version-consistency/version-consistency-test-workflow.json index 02ec351..15cd5ca 100644 --- a/core/Workflows/version-consistency/version-consistency-test-workflow.json +++ b/core/Workflows/version-consistency/version-consistency-test-workflow.json @@ -149,4 +149,4 @@ } ] } -} +} \ No newline at end of file diff --git a/core/Workflows/view-function-extension/.meta/view-function-extension-test-workflow.diagram.json b/core/Workflows/view-function-extension/.meta/view-function-extension-test-workflow.diagram.json index 00a4467..e4fa1fe 100644 --- a/core/Workflows/view-function-extension/.meta/view-function-extension-test-workflow.diagram.json +++ b/core/Workflows/view-function-extension/.meta/view-function-extension-test-workflow.diagram.json @@ -1,3 +1,44 @@ { - "nodePos": {} + "nodePos": { + "__start__": { + "x": 90, + "y": 0 + }, + "view-test-state": { + "x": 710, + "y": 130 + }, + "html-view-state": { + "x": 780, + "y": 360 + }, + "markdown-view-state": { + "x": 770, + "y": 610 + }, + "deeplink-view-state": { + "x": 790, + "y": 890 + }, + "http-view-state": { + "x": 790, + "y": 1110 + }, + "urn-wizard-state": { + "x": 780, + "y": 1370 + }, + "completed-state": { + "x": 0, + "y": 1480 + }, + "wizard-step-state": { + "x": 160, + "y": 1190 + }, + "urn-view-state": { + "x": 630, + "y": 1510 + } + } } \ No newline at end of file diff --git a/core/Workflows/view-function-extension/view-function-extension-test-workflow.json b/core/Workflows/view-function-extension/view-function-extension-test-workflow.json index 86211ef..bc411a7 100644 --- a/core/Workflows/view-function-extension/view-function-extension-test-workflow.json +++ b/core/Workflows/view-function-extension/view-function-extension-test-workflow.json @@ -4,6 +4,7 @@ "flowVersion": "1.0.0", "domain": "core", "version": "1.0.0", + "_comment": "vfe-global-extension (Type 1 Global, Scope 3 Everywhere) intentionally NOT referenced here. Type 1 extensions auto-run on every flow GET endpoint regardless of workflow registration; the integration test verifies this implicit behavior in GlobalExtension_AppliesImplicitly_WithoutWorkflowReference.", "tags": [ "integration-test", "view", @@ -12,14 +13,17 @@ "json-view", "html-view", "markdown-view", - "rule-based-view", + "deeplink-view", + "http-view", + "urn-view", "display-modes", - "single-task-function", - "multi-task-function", - "output-handler", - "global-extension", - "requested-extension", - "features" + "vfe-single-task-function", + "vfe-multi-task-function", + "vfe-workflow-function", + "vfe-domain-function", + "vfe-global-and-requested-extension", + "vfe-defined-flows-extension", + "vfe-defined-flow-and-requested-extension" ], "attributes": { "type": "F", @@ -27,40 +31,50 @@ "labels": [ { "language": "en-US", - "label": "View, Function and Extension Integration Test" + "label": "View, Function and Extension Comprehensive Integration Test" } ], "functions": [ { - "key": "single-task-function", + "key": "vfe-single-task-function", "domain": "core", "version": "1.0.0", "flow": "sys-functions" }, { - "key": "multi-task-function", + "key": "vfe-multi-task-function", "domain": "core", "version": "1.0.0", "flow": "sys-functions" - } - ], - "features": [ + }, { - "key": "global-extension", + "key": "vfe-workflow-function", "domain": "core", "version": "1.0.0", - "flow": "sys-extensions" + "flow": "sys-functions" + }, + { + "key": "vfe-domain-function", + "domain": "core", + "version": "1.0.0", + "flow": "sys-functions" } ], "extensions": [ { - "key": "global-extension", + "key": "vfe-global-and-requested-extension", "domain": "core", "version": "1.0.0", "flow": "sys-extensions" }, { - "key": "requested-extension", + "key": "vfe-defined-flows-extension", + "domain": "core", + "version": "1.0.0", + "flow": "sys-extensions" + }, + { + "key": "vfe-defined-flow-and-requested-extension", "domain": "core", "version": "1.0.0", "flow": "sys-extensions" @@ -103,7 +117,7 @@ "labels": [ { "language": "en-US", - "label": "JSON View State" + "label": "JSON View State (type=1, display=full-page)" } ], "view": { @@ -119,56 +133,127 @@ "onExits": [], "transitions": [ { - "key": "auto-to-multi-view", - "target": "multi-view-state", - "triggerType": 1, - "versionStrategy": "Major", + "key": "continue-to-html", + "target": "html-view-state", + "triggerType": 0, + "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Auto to Multi View (WebPlatformRule)" + "label": "Continue to HTML View State (transition view: json-view)" } ], - "rule": { - "location": "./src/WebPlatformRule.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFdlYlBsYXRmb3JtUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBwbGF0Zm9ybSA9ICIiOwogICAgICAgIHZhciBoZWFkZXJzID0gY29udGV4dC5IZWFkZXJzOwogICAgICAgIGlmIChoZWFkZXJzICE9IG51bGwgJiYgSGFzUHJvcGVydHkoaGVhZGVycywgInBsYXRmb3JtIikpCiAgICAgICAgICAgIHBsYXRmb3JtID0gQ29udmVydC5Ub1N0cmluZyhoZWFkZXJzLnBsYXRmb3JtKSA/PyAiIjsKCiAgICAgICAgdmFyIGlzV2ViID0gc3RyaW5nLkVxdWFscyhwbGF0Zm9ybSwgIndlYiIsIFN0cmluZ0NvbXBhcmlzb24uT3JkaW5hbElnbm9yZUNhc2UpOwogICAgICAgIExvZ0luZm9ybWF0aW9uKCQiV2ViUGxhdGZvcm1SdWxlOiBwbGF0Zm9ybT17cGxhdGZvcm19LCBpc1dlYj17aXNXZWJ9Iik7CgogICAgICAgIC8vIEludGVncmF0aW9uIHRlc3RzOiB0cmVhdCBhcyBzYXRpc2ZpZWQgc28gZmxvd3MgcGFzcyB3aXRob3V0IGEgY2xpZW50IGhlYWRlci4KICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" + "schema": null, + "view": { + "view": { + "key": "json-view", + "domain": "core", + "version": "1.0.0", + "flow": "sys-views" + }, + "loadData": true }, + "mapping": null, "onExecutionTasks": [] - }, + } + ] + }, + { + "key": "html-view-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ { - "key": "default-auto-fallback", - "target": "completed-state", - "triggerType": 1, - "triggerKind": 10, + "language": "en-US", + "label": "HTML View State (type=2, display=popup)" + } + ], + "view": { + "view": { + "key": "html-view", + "domain": "core", + "version": "1.0.0", + "flow": "sys-views" + } + }, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "to-markdown", + "target": "markdown-view-state", + "triggerType": 0, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Default Auto Fallback (AlwaysTrueRule)" + "label": "Continue to Markdown View State" } ], - "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBhc3luYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KfQo=" - }, + "schema": null, + "view": null, + "mapping": null, "onExecutionTasks": [] } ] }, { - "key": "multi-view-state", + "key": "markdown-view-state", "stateType": 2, "subType": 0, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "HTML View State" + "label": "Markdown View State (type=3, display=bottom-sheet)" } ], "view": { "view": { - "key": "html-view", + "key": "markdown-view", + "domain": "core", + "version": "1.0.0", + "flow": "sys-views" + } + }, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "to-deeplink", + "target": "deeplink-view-state", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Continue to Deeplink View State" + } + ], + "schema": null, + "view": null, + "mapping": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "deeplink-view-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Deeplink View State (type=4, display=top-sheet)" + } + ], + "view": { + "view": { + "key": "deeplink-view", "domain": "core", "version": "1.0.0", "flow": "sys-views" @@ -179,14 +264,14 @@ "onExits": [], "transitions": [ { - "key": "manual-to-wizard", - "target": "wizard-state", + "key": "to-http", + "target": "http-view-state", "triggerType": 0, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Continue to Wizard State" + "label": "Continue to HTTP View State" } ], "schema": null, @@ -197,14 +282,96 @@ ] }, { - "key": "wizard-state", + "key": "http-view-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "HTTP View State (type=5, display=drawer)" + } + ], + "view": { + "view": { + "key": "http-view", + "domain": "core", + "version": "1.0.0", + "flow": "sys-views" + } + }, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "to-urn", + "target": "urn-view-state", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Continue to URN View State" + } + ], + "schema": null, + "view": null, + "mapping": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "urn-view-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "URN View State (type=6, display=inline)" + } + ], + "view": { + "view": { + "key": "urn-view", + "domain": "core", + "version": "1.0.0", + "flow": "sys-views" + } + }, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "to-wizard", + "target": "wizard-step-state", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Continue to Wizard Step State" + } + ], + "schema": null, + "view": null, + "mapping": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "wizard-step-state", "stateType": 5, "subType": 0, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Wizard State" + "label": "Wizard Step State (stateType=5, transition view rendered)" } ], "view": null, @@ -213,24 +380,25 @@ "onExits": [], "transitions": [ { - "key": "complete-with-markdown-view", + "key": "complete", "target": "completed-state", "triggerType": 0, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Complete with Markdown View" + "label": "Complete Workflow" } ], "schema": null, "view": { "view": { - "key": "markdown-view", + "key": "json-view", "domain": "core", "version": "1.0.0", "flow": "sys-views" - } + }, + "loadData": true }, "mapping": null, "onExecutionTasks": [] @@ -256,4 +424,4 @@ } ] } -} +} \ No newline at end of file diff --git a/docs/integration-test-documentation.md b/docs/integration-test-documentation.md index a4f7055..8a10f2a 100644 --- a/docs/integration-test-documentation.md +++ b/docs/integration-test-documentation.md @@ -12,7 +12,7 @@ Bu dokuman, `core` projesindeki tum integration test workflow'larini, icerdikler | 2 | SubFlow & SubProcess | `subflow-orchestration-parent` + child + grandchild | Parent-child-grandchild zinciri, parent shared transitions, **child shared transition**, **cancel cascade** (child/grandchild **cancelled** final state'leri), **effective state**, **updateData (SubFlow)**, **subFlow.overrides** (timeout, states.queryRoles, transitions.roles) | | 3 | Task Execution | `task-execution-test-workflow` + `task-target-workflow` + `extended-tasks-test-workflow` | HTTP/Script/StartFlow/GetInstanceData/Notification/TriggerTransition/SubProcess/GetInstances; Dapr HTTP/Service/PubSub (`extended-tasks-test-workflow`); Mocklab + Dapr YAML | | 4 | Error Boundary | `error-boundary-test-workflow` | Retry, Ignore, **Rollback**, **Log**, **Notify** (action:4) aksiyonlari, retry policy, priority, **timeoutPolicy** (onTimeout), **errorHandlerRule** (errorTypes/errorCodes) | -| 5 | View, Function & Extension | `view-function-extension-test-workflow` | View tipleri, display modlari, function, extension, wizard state, **features** referansi | +| 5 | View, Function & Extension (Comprehensive) | `view-function-extension-test-workflow` | 6 view tipi (JSON/HTML/MD/DeepLink/HTTP/URN), 6 display modu, 4 extension tipi (4 type × 3 scope), 3 function scope (I/F/D), wizard state, implicit global extension, `?extensions=` sorgusu, `functions/view` icerik dogrulamasi | | 6 | Schema & Data | `schema-data-test-workflow` | Master schema, transition schema, updateData, field roles | | 7 | Instance Management | `instance-management-test-workflow` | Filtering, pagination, sorting, timeout, idempotent start, **subType 4/5/6** (suspended/busy/human) | | 8 | Flow Types | `core-flow-test` + `subprocess-flow-test` + `flow-flow-test` + `subflow-flow-test` | **Core (C)**, **SubProcess (P)**, **Flow (F)** ve **SubFlow (S)** workflow tipleri | @@ -29,10 +29,10 @@ Bu dokuman, `core` projesindeki tum integration test workflow'larini, icerdikler core/ ├── Workflows/ (15 workflow - 13 mevcut + 2 yeni version-consistency) ├── Tasks/ (22 task - 21 mevcut + 1 yeni version-consistency; Dapr Binding task kaldirildi) -├── Views/ (3 view) +├── Views/ (6 view) ├── Schemas/ (2 schema) -├── Functions/ (2 function) -├── Extensions/ (2 extension) +├── Functions/ (4 function — 2 instance, 1 workflow, 1 domain scope) +├── Extensions/ (4 extension — 4 type, 3 scope) ├── etc/dapr/components/ (Dapr binding ve pubsub YAML) └── doc/ └── integration-test-documentation.md @@ -574,93 +574,156 @@ error-boundary-test-workflow (F) --- -## Grup 5: View, Function & Extension +## Grup 5: View, Function & Extension (Comprehensive) **Workflow:** `view-function-extension-test-workflow` (type: F) -**Amac:** View tipleri, display modlari, function cagrilari, extension mekanizmasi, wizard state'i ve **features** referansini test eder. +**Amac:** Tum 6 view icerik tipi (JSON/HTML/Markdown/DeepLink/HTTP/URN), tum 6 display modu (full-page/popup/bottom-sheet/top-sheet/drawer/inline), tum 4 extension tipi (Global/GlobalAndRequested/DefinedFlows/DefinedFlowAndRequested), tum 3 extension scope (GetInstance/GetAllInstances/Everywhere), tum 3 function scope (I/F/D), wizard state kisitlamasi, `features` referansi ve `functions/view` icerik dogrulamasini kapsamli olarak test eder. -### Agac Yapisi +**Not:** `vfe-global-extension` (Type 1, Scope 3) workflow `extensions` dizisinde **tanimli degildir** — Global extension tanimina gore acik referans olmadan tum flow'larda otomatik uygulanmalidir. Bu implicit davranis test icinde dogrulanir. + +### Agac Yapisi (7 state, lineer zincir) ``` view-function-extension-test-workflow (F) │ ├── [startTransition] start-view-function-extension-test │ └── onExecutionTasks: -│ └── vfe-script-task + InitVfeMapping.csx +│ └── vfe-script-task + InitVfeMapping.csx (vfeTestStarted=true) │ ├── [functions] -│ ├── single-task-function (scope: Instance) +│ ├── vfe-single-task-function (scope: I — Instance) │ │ └── task: vfe-script-task + FunctionSingleTaskMapping.csx -│ └── multi-task-function (scope: Instance) -│ ├── onExecutionTasks: -│ │ ├── order:1 vfe-script-task + FunctionMultiTask1Mapping.csx -│ │ └── order:2 vfe-http-task + FunctionMultiTask2Mapping.csx -│ └── output: FunctionOutputMapping.csx (IOutputHandler) -│ -├── [features] -│ └── global-extension (sys-extensions referansi — features array'inde) -│ -├── [extensions] -│ ├── global-extension (type:1 Global, scope:3 Everywhere) -│ │ └── task: vfe-script-task + GlobalExtensionMapping.csx -│ └── requested-extension (type:4 DefinedFlowAndRequested, scope:1 GetInstance) -│ └── task: vfe-script-task + RequestedExtensionMapping.csx +│ │ (singleTaskFunction=true, executedAt) +│ ├── vfe-multi-task-function (scope: I — Instance) +│ │ ├── onExecutionTasks: +│ │ │ ├── order:1 vfe-script-task + FunctionMultiTask1Mapping.csx +│ │ │ └── order:2 vfe-script-task-2 + FunctionMultiTask2Mapping.csx +│ │ └── output: FunctionOutputMapping.csx (IOutputHandler, aggregated=true) +│ ├── vfe-workflow-function (scope: F — Workflow) +│ │ └── task: vfe-script-task + VfeWorkflowFunctionMapping.csx +│ │ (functionScope="F", workflowFunction=true) +│ └── vfe-domain-function (scope: D — Domain/Global) +│ └── task: vfe-script-task + VfeDomainFunctionMapping.csx +│ (functionScope="D", domainFunction=true) +│ +├── [extensions] (workflow dizisinde 3 tanesi; Type 1 implicit) +│ ├── vfe-global-and-requested-extension (type:2, scope:1 GetInstance) +│ ├── vfe-defined-flows-extension (type:3, scope:2 GetAllInstances) +│ └── vfe-defined-flow-and-requested-extension (type:4, scope:1 GetInstance) +│ +├── [implicit — workflow'da referans edilmez] +│ └── vfe-global-extension (type:1 Global, scope:3 Everywhere) +│ → tum GET isteklerinde otomatik uygulanir │ ├── view-test-state (Initial, stateType:1) │ ├── view: json-view (type:1 JSON, display: full-page) │ └── Transitions: -│ ├── auto-to-multi-view (Auto, triggerType:1) → multi-view-state -│ │ └── rule: WebPlatformRule.csx (IConditionMapping, platform kontrolu) +│ ├── auto-to-html-view (Auto, triggerType:1) → html-view-state +│ │ └── rule: WebPlatformRule.csx (IConditionMapping) │ └── default-auto-fallback (Auto, triggerType:1, triggerKind:10) → completed-state -│ └── rule: AlwaysTrueRule.csx (fallback) +│ └── rule: AlwaysTrueRule.csx │ -├── multi-view-state (Intermediate, stateType:2) +├── html-view-state (Intermediate, stateType:2) │ ├── view: html-view (type:2 HTML, display: popup) │ └── Transitions: -│ └── manual-to-wizard (Manual) → wizard-state +│ └── go-to-markdown (Manual) → markdown-view-state +│ +├── markdown-view-state (Intermediate, stateType:2) +│ ├── view: markdown-view (type:3 Markdown, display: bottom-sheet) +│ └── Transitions: +│ └── go-to-deeplink (Manual) → deeplink-view-state │ -├── wizard-state (Wizard, stateType:5) +├── deeplink-view-state (Intermediate, stateType:2) +│ ├── view: deeplink-view (type:4 DeepLink, display: top-sheet) +│ │ └── content: {"href": "myapp://consent/approve?token=abc123"} │ └── Transitions: -│ └── complete-with-markdown-view (Manual) → completed-state -│ └── view: markdown-view (type:3 Markdown, display: bottom-sheet) +│ └── go-to-http (Manual) → http-view-state +│ +├── http-view-state (Intermediate, stateType:2) +│ ├── view: http-view (type:5 HTTP, display: drawer) +│ │ └── content: {"href": "https://example.com/consent/form?id=xyz"} +│ └── Transitions: +│ └── go-to-urn-wizard (Manual) → urn-wizard-state +│ +├── urn-wizard-state (Wizard, stateType:5) +│ ├── view: urn-view (type:6 URN, display: inline) +│ │ └── content: {"urn": "urn:openbanking:consent:approve:v1"} +│ └── Transitions: +│ └── complete-wizard (Manual) → completed-state +│ (en fazla 1 transition — wizard kisitlamasi) │ └── completed-state (Final/Success, stateType:3, subType:1) ``` -### Test Edilen Ozellikler +### Test Edilen Ozellikler — View + +| Ozellik | Nasil Test Edilir | Ilgili Eleman | +|---------|-------------------|---------------| +| JSON View (type:1) | State view + `functions/view` content assertion | json-view → view-test-state | +| HTML View (type:2) | State view + `functions/view` content non-empty | html-view → html-view-state | +| Markdown View (type:3) | State view + `functions/view` content non-empty | markdown-view → markdown-view-state | +| DeepLink View (type:4) | State view + `functions/view` content `href` assertion | deeplink-view → deeplink-view-state | +| HTTP View (type:5) | State view + `functions/view` content `href` assertion | http-view → http-view-state | +| URN View (type:6) | State view + `functions/view` content `urn` assertion | urn-view → urn-wizard-state | +| Display: full-page | json-view | view-test-state | +| Display: popup | html-view | html-view-state | +| Display: bottom-sheet | markdown-view | markdown-view-state | +| Display: top-sheet | deeplink-view | deeplink-view-state | +| Display: drawer | http-view | http-view-state | +| Display: inline | urn-view | urn-wizard-state | +| Wizard state (stateType:5) | En fazla 1 transition kisitlamasi | urn-wizard-state | + +### Test Edilen Ozellikler — Extension + +| Ozellik | Nasil Test Edilir | Ilgili Eleman | +|---------|-------------------|---------------| +| Type 1 Global (scope:3 Everywhere) | Workflow `extensions`'da **tanimli degil**; `functions/data`'da implicit gozukme + `vfeExtensionType=global` marker | vfe-global-extension | +| Type 1 Scope 3 — state response | `functions/state` yanitinda da extensions gorunuyor mu kontrolu (runtime davranisina bagli) | vfe-global-extension | +| Type 2 GlobalAndRequested (scope:1 GetInstance) | Otomatik uygulanma + `?extensions=` query ile sorgulanabilirlik | vfe-global-and-requested-extension | +| Type 3 DefinedFlows (scope:2 GetAllInstances) | Otomatik uygulanma + list instances yaniti zenginlestirme | vfe-defined-flows-extension | +| Type 4 DefinedFlowAndRequested (scope:1 GetInstance) | `functions/data`'da **gorunMEMELI**; `?extensions=` ile sorgulaninca gorunMELI | vfe-defined-flow-and-requested-extension | +| Extension type marker | Her extension'in `vfeExtensionType` alani assertion'i | Tum 4 extension mapping'i | + +### Test Edilen Ozellikler — Function + +| Ozellik | Nasil Test Edilir | Ilgili Eleman | +|---------|-------------------|---------------| +| Instance scope (I) — single task | `functions/{name}` cagrisi + `singleTaskFunction=true` marker | vfe-single-task-function | +| Instance scope (I) — multi task + IOutputHandler | `functions/{name}` cagrisi + `aggregated=true` marker | vfe-multi-task-function + FunctionOutputMapping | +| Workflow scope (F) | `workflows/{key}/functions/{name}` cagrisi + `functionScope="F"` marker | vfe-workflow-function | +| Domain scope (D) | `functions/{name}` (domain-level) cagrisi + `functionScope="D"` marker | vfe-domain-function | + +### Test Edilen Ozellikler — Diger | Ozellik | Nasil Test Edilir | Ilgili Eleman | |---------|-------------------|---------------| -| JSON View (type:1) | State'e JSON view baglama | json-view → view-test-state | -| HTML View (type:2) | State'e HTML view baglama | html-view → multi-view-state | -| Markdown View (type:3) | Transition'a view baglama | markdown-view → complete-with-markdown-view | -| Display modlari | full-page, popup, bottom-sheet | 3 farkli view | -| Wizard State (stateType:5) | En fazla 1 transition kisitlamasi | wizard-state | -| Transition view | Transition uzerinde view gosterimi | complete-with-markdown-view | -| IConditionMapping (transition rule) | Auto transition'da rule kullanimi | WebPlatformRule.csx | -| triggerKind:10 (varsayilan fallback) | Complementary auto transitions | default-auto-fallback | -| Single-task function | Tek task'li function tanimlama | single-task-function | -| Multi-task function | Birden fazla task'li function | multi-task-function | -| IOutputHandler | Function ciktisini birlestirme | FunctionOutputMapping.csx | -| Global extension (type:1) | Tum isteklerde calisan extension | global-extension | -| Requested extension (type:4) | Talep edildiginde calisan extension | requested-extension | -| Extension scope | Everywhere vs GetInstance | scope:3 vs scope:1 | -| **Features referansi** | features array'inde extension referansi | features: global-extension | +| IConditionMapping (auto transition rule) | WebPlatformRule ile otomatik gecis | view-test-state → html-view-state | +| triggerKind:10 (varsayilan fallback) | Complementary auto transition | default-auto-fallback | +| startTransition onExecutionTasks | Workflow baslatilirken `vfeTestStarted=true` set | InitVfeMapping.csx | +| Happy path — status C | Tum state zincirinden gecip completed | 7 state lineer zincir | ### Kullanilan Bilesenler | Tip | Anahtar | Dosya | |-----|---------|-------| | Task | `vfe-script-task` (type:7) | Tasks/view-function-extension/vfe-script-task.json | +| Task | `vfe-script-task-2` (type:7) | Tasks/view-function-extension/vfe-script-task-2.json | | Task | `vfe-http-task` (type:6) | Tasks/view-function-extension/vfe-http-task.json | -| View | `json-view` | Views/view-function-extension/json-view.json | -| View | `html-view` | Views/view-function-extension/html-view.json | -| View | `markdown-view` | Views/view-function-extension/markdown-view.json | -| Function | `single-task-function` | Functions/view-function-extension/single-task-function.json | -| Function | `multi-task-function` | Functions/view-function-extension/multi-task-function.json | -| Extension | `global-extension` | Extensions/view-function-extension/global-extension.json | -| Extension | `requested-extension` | Extensions/view-function-extension/requested-extension.json | +| View | `json-view` (type:1, display:full-page) | Views/view-function-extension/json-view.json | +| View | `html-view` (type:2, display:popup) | Views/view-function-extension/html-view.json | +| View | `markdown-view` (type:3, display:bottom-sheet) | Views/view-function-extension/markdown-view.json | +| View | `deeplink-view` (type:4, display:top-sheet) | Views/view-function-extension/deeplink-view.json | +| View | `http-view` (type:5, display:drawer) | Views/view-function-extension/http-view.json | +| View | `urn-view` (type:6, display:inline) | Views/view-function-extension/urn-view.json | +| Function | `vfe-single-task-function` (scope:I) | Functions/view-function-extension/vfe-single-task-function.json | +| Function | `vfe-multi-task-function` (scope:I) | Functions/view-function-extension/vfe-multi-task-function.json | +| Function | `vfe-workflow-function` (scope:F) | Functions/view-function-extension/vfe-workflow-function.json | +| Function | `vfe-domain-function` (scope:D) | Functions/view-function-extension/vfe-domain-function.json | +| Extension | `vfe-global-extension` (type:1, scope:3) | Extensions/view-function-extension/vfe-global-extension.json | +| Extension | `vfe-global-and-requested-extension` (type:2, scope:1) | Extensions/view-function-extension/vfe-global-and-requested-extension.json | +| Extension | `vfe-defined-flows-extension` (type:3, scope:2) | Extensions/view-function-extension/vfe-defined-flows-extension.json | +| Extension | `vfe-defined-flow-and-requested-extension` (type:4, scope:1) | Extensions/view-function-extension/vfe-defined-flow-and-requested-extension.json | | CSX | InitVfeMapping | Workflows/view-function-extension/src/InitVfeMapping.csx | | CSX | WebPlatformRule | Workflows/view-function-extension/src/WebPlatformRule.csx | | CSX | AlwaysTrueRule | Workflows/view-function-extension/src/AlwaysTrueRule.csx | @@ -668,8 +731,44 @@ view-function-extension-test-workflow (F) | CSX | FunctionMultiTask1Mapping | Functions/view-function-extension/src/FunctionMultiTask1Mapping.csx | | CSX | FunctionMultiTask2Mapping | Functions/view-function-extension/src/FunctionMultiTask2Mapping.csx | | CSX | FunctionOutputMapping | Functions/view-function-extension/src/FunctionOutputMapping.csx | -| CSX | GlobalExtensionMapping | Extensions/view-function-extension/src/GlobalExtensionMapping.csx | -| CSX | RequestedExtensionMapping | Extensions/view-function-extension/src/RequestedExtensionMapping.csx | +| CSX | VfeWorkflowFunctionMapping | Functions/view-function-extension/src/VfeWorkflowFunctionMapping.csx | +| CSX | VfeDomainFunctionMapping | Functions/view-function-extension/src/VfeDomainFunctionMapping.csx | +| CSX | VfeGlobalExtensionMapping | Extensions/view-function-extension/src/VfeGlobalExtensionMapping.csx | +| CSX | VfeGlobalAndRequestedExtensionMapping | Extensions/view-function-extension/src/VfeGlobalAndRequestedExtensionMapping.csx | +| CSX | VfeDefinedFlowsExtensionMapping | Extensions/view-function-extension/src/VfeDefinedFlowsExtensionMapping.csx | +| CSX | VfeDefinedFlowAndRequestedExtensionMapping | Extensions/view-function-extension/src/VfeDefinedFlowAndRequestedExtensionMapping.csx | + +### C# Integration Test Dosyasi + +`tests/Core/Tests/view-function-extension-test-workflow/ViewFunctionExtensionTestWorkflowTests.cs` — 18 `[Fact]` metodu: + +| Test | Kisa Aciklama | +|------|---------------| +| HappyPath_AllStateTransitions_ReachesCompletedWithStatusC | 7 state zinciri, status=C | +| StartTransitionMapping_SetsVfeTestStarted | vfeTestStarted=true marker | +| JsonViewState_Type1_FullPage_HasView | JSON view + content non-empty | +| HtmlViewState_Type2_Popup_HasView | HTML view + content non-empty | +| MarkdownViewState_Type3_BottomSheet_HasView | Markdown view + content non-empty | +| DeeplinkViewState_Type4_TopSheet_HasView | DeepLink view + content href assertion | +| HttpViewState_Type5_Drawer_HasView | HTTP view + content href assertion | +| UrnWizardState_Type6_Inline_HasViewAndSingleTransition | URN view + content urn assertion + wizard 1 transition | +| ViewFunction_ReturnsViewPayload | functions/view genel kontrol | +| GlobalExtension_Type1_AppliesImplicitly | implicit gozukme + state response scope 3 | +| GlobalAndRequestedExtension_Type2_AutoAppliesAndQueryable | otomatik + ?extensions= sorgu | +| DefinedFlowsExtension_Type3_AutoAppliesOnDefinedFlow | otomatik uygulanma | +| DefinedFlowAndRequestedExtension_Type4_OnlyAppearsWhenRequested | absent + requested | +| DefinedFlowsExtension_Scope2_GetAllInstances_EnrichesListEndpoint | list enrichment | +| SingleTaskFunction_InstanceScope_ReturnsResponse | singleTaskFunction=true | +| MultiTaskFunction_InstanceScope_AggregatesResponses | aggregated=true | +| WorkflowFunction_WorkflowScope_ReturnsResponse | functionScope="F" | +| DomainFunction_DomainScope_ReturnsResponse | functionScope="D" | +| AutoTransition_WebPlatformRule_ReachesHtmlViewState | auto transition smoke | + +### HTTP Test Dosyasi (`api-tests/view-function-extension/view-function-extension-test-workflow.http`) + +22 adimli akis: workflow baslatma, 6 state uzerinden gecis, 4 function cagrisi (instance, workflow, domain scope), 4 extension dogrulamasi (?extensions= sorgusu dahil), view endpoint'leri. + +### Postman Collection (`api-tests/view-function-extension/postman-view-function-extension.json`) --- @@ -1264,11 +1363,17 @@ Her grubun hangi vNext ozelliklerini test ettigini gosteren matris. **Gn** sutun | **Notify action (action:4)** | | | | X | | | | | | | | **timeoutPolicy (onTimeout)** | | | | X | | | | | | | | **errorHandlerRule (errorTypes/errorCodes)** | | | | X | | | | | | | -| View (JSON/HTML/MD) | | | | | X | | | | | | -| Display modlari | | | | | X | | | | | | -| Function (single/multi) | | | | | X | | | | | | -| Extension (global/req) | | | | | X | | | | | | -| **Features referansi** | | | | | X | | | | | | +| View (6 tip: JSON/HTML/MD/DeepLink/HTTP/URN) | | | | | X | | | | | | +| Display modlari (6: full-page/popup/bottom-sheet/top-sheet/drawer/inline) | | | | | X | | | | | | +| `functions/view` icerik dogrulamasi (type, display, content) | | | | | X | | | | | | +| Function scope I (single + multi + IOutputHandler) | | | | | X | | | | | | +| Function scope F (workflow) | | | | | X | | | | | | +| Function scope D (domain/global) | | | | | X | | | | | | +| Extension type 1 (Global, implicit, scope 3 Everywhere) | | | | | X | | | | | | +| Extension type 2 (GlobalAndRequested, scope 1 GetInstance) | | | | | X | | | | | | +| Extension type 3 (DefinedFlows, scope 2 GetAllInstances) | | | | | X | | | | | | +| Extension type 4 (DefinedFlowAndRequested, scope 1 GetInstance) | | | | | X | | | | | | +| Extension `?extensions=` sorgu parametresi | | | | | X | | | | | | | Workflow timeout | | | | | | | X | | | | | Idempotent start | X | | | | | | X | | | | | Instance filtreleme | | | X | | | | X | | | | diff --git a/tests/Core/Helpers/DomainFunctionHelper.cs b/tests/Core/Helpers/DomainFunctionHelper.cs new file mode 100644 index 0000000..173506c --- /dev/null +++ b/tests/Core/Helpers/DomainFunctionHelper.cs @@ -0,0 +1,33 @@ +using System.Net; +using System.Text.Json; +using VNext.Testing.Sdk.Client; +using Xunit; + +namespace Core.IntegrationTests.Helpers; + +/// +/// Helpers for domain-scoped function calls (GET .../functions/{functionName}) +/// that are NOT tied to a specific workflow key. +/// +public static class DomainFunctionHelper +{ + public static async Task CallDomainScopeFunctionAsync( + VNextApiClient api, + string domain, + string apiVersion, + string functionName, + Dictionary? queryParams = null + ) + { + var qs = queryParams != null + ? string.Join("&", queryParams.Select(kv => $"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value)}")) + : ""; + var path = $"/api/v{apiVersion}/{domain}/functions/{Uri.EscapeDataString(functionName)}"; + if (!string.IsNullOrEmpty(qs)) + path += "?" + qs; + + var response = await api.GetRawAsync(path); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + return response.Body; + } +} diff --git a/tests/Core/Helpers/WorkflowInstanceTestHelper.cs b/tests/Core/Helpers/WorkflowInstanceTestHelper.cs index bc166df..fc2e2fa 100644 --- a/tests/Core/Helpers/WorkflowInstanceTestHelper.cs +++ b/tests/Core/Helpers/WorkflowInstanceTestHelper.cs @@ -161,6 +161,36 @@ public async Task GetAttributesAsync(string instanceId) return attributes; } + /// + /// Returns the full response body from GET .../instances/{id}. + /// + public async Task GetInstanceBodyAsync(string instanceId) + { + var response = await _api.GetInstanceAsync(_workflowKey, instanceId); + return response.Body; + } + + /// + /// Returns the full response body from GET .../instances/{id}?{query} via raw path. + /// Useful for adding query parameters like ?extensions=.... + /// + public async Task GetInstanceRawAsync( + string instanceId, + Dictionary? queryParams = null + ) + { + var qs = queryParams != null + ? string.Join("&", queryParams.Select(kv => $"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value)}")) + : ""; + var path = $"/api/v{_apiVersion}/{_domain}/workflows/{Uri.EscapeDataString(_workflowKey)}/instances/{Uri.EscapeDataString(instanceId)}"; + if (!string.IsNullOrEmpty(qs)) + path += "?" + qs; + + var response = await _api.GetRawAsync(path); + Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode); + return response.Body; + } + /// /// Calls a named instance function (GET .../functions/{functionName}) and returns the response body. /// Useful for view, data, custom functions, etc. @@ -182,6 +212,25 @@ public async Task CallFunctionAsync( return response.Body; } + /// + /// Calls a workflow-scoped function (GET .../workflows/{workflowKey}/functions/{functionName}). + /// + public async Task CallWorkflowScopeFunctionAsync( + string functionName, + Dictionary? queryParams = null, + Dictionary? headers = null + ) + { + var response = await _api.CallWorkflowFunctionAsync( + _workflowKey, + functionName, + queryParams, + headers + ); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + return response.Body; + } + /// /// Calls GET .../instances with any subset of filter / sort / page / /// pageSize query parameters and asserts HTTP 200 before returning the response body. diff --git a/tests/Core/Tests/view-function-extension-test-workflow/VfeExtensionAssertions.cs b/tests/Core/Tests/view-function-extension-test-workflow/VfeExtensionAssertions.cs new file mode 100644 index 0000000..a68c33d --- /dev/null +++ b/tests/Core/Tests/view-function-extension-test-workflow/VfeExtensionAssertions.cs @@ -0,0 +1,98 @@ +using System.Text.Json; +using Xunit; + +namespace Core.IntegrationTests.Tests.ViewFunctionExtensionTestWorkflow; + +/// +/// Extension-specific assertions for view-function-extension-test-workflow. +/// +/// Runtime converts kebab-case extension keys to camelCase in the response. +/// For example, vfe-global-extension becomes vfeGlobalExtension. +/// All lookups try both the original key and the camelCase variant. +/// +/// +public static class VfeExtensionAssertions +{ + public static void AssertExtensionPresent(JsonElement body, string extensionKey) + { + Assert.True( + TryGetExtensionData(body, extensionKey, out _), + $"Extension '{extensionKey}' (or '{ToCamelCase(extensionKey)}') should be present in the response." + ); + } + + public static void AssertExtensionAbsent(JsonElement body, string extensionKey) + { + Assert.False( + TryGetExtensionData(body, extensionKey, out _), + $"Extension '{extensionKey}' (or '{ToCamelCase(extensionKey)}') should NOT be present in the response." + ); + } + + public static void AssertExtensionTypeMarker( + JsonElement body, + string extensionKey, + string expectedType + ) + { + Assert.True( + TryGetExtensionData(body, extensionKey, out var extData), + $"Extension '{extensionKey}' (or '{ToCamelCase(extensionKey)}') should be present." + ); + Assert.True( + extData.TryGetProperty("vfeExtensionType", out var typeEl) + && typeEl.ValueKind == JsonValueKind.String, + $"Extension '{extensionKey}' should have 'vfeExtensionType' string property." + ); + Assert.Equal(expectedType, typeEl.GetString()); + } + + private static bool TryGetExtensionData( + JsonElement body, + string extensionKey, + out JsonElement extensionData + ) + { + extensionData = default; + var camelKey = ToCamelCase(extensionKey); + + if (body.ValueKind == JsonValueKind.Object + && body.TryGetProperty("extensions", out var extensions) + && extensions.ValueKind == JsonValueKind.Object) + { + if (extensions.TryGetProperty(extensionKey, out extensionData)) + return true; + if (extensions.TryGetProperty(camelKey, out extensionData)) + return true; + } + + if (body.ValueKind == JsonValueKind.Object + && body.TryGetProperty("data", out var data) + && data.ValueKind == JsonValueKind.Object + && data.TryGetProperty("extensions", out var nestedExtensions) + && nestedExtensions.ValueKind == JsonValueKind.Object) + { + if (nestedExtensions.TryGetProperty(extensionKey, out extensionData)) + return true; + if (nestedExtensions.TryGetProperty(camelKey, out extensionData)) + return true; + } + + return false; + } + + /// + /// Converts kebab-case string to camelCase: vfe-global-extension → vfeGlobalExtension. + /// + private static string ToCamelCase(string kebab) + { + var parts = kebab.Split('-'); + if (parts.Length <= 1) + return kebab; + return parts[0] + string.Concat( + parts.Skip(1).Select(p => + p.Length == 0 ? p : char.ToUpperInvariant(p[0]) + p[1..] + ) + ); + } +} diff --git a/tests/Core/Tests/view-function-extension-test-workflow/VfeFunctionAssertions.cs b/tests/Core/Tests/view-function-extension-test-workflow/VfeFunctionAssertions.cs new file mode 100644 index 0000000..c34a4ce --- /dev/null +++ b/tests/Core/Tests/view-function-extension-test-workflow/VfeFunctionAssertions.cs @@ -0,0 +1,88 @@ +using System.Text.Json; +using Xunit; + +namespace Core.IntegrationTests.Tests.ViewFunctionExtensionTestWorkflow; + +/// +/// Function-specific assertions for view-function-extension-test-workflow. +/// +/// Runtime wraps function output in a camelCase wrapper keyed by the task key. +/// For example, task key vfe-script-task → wrapper property vfeScriptTask. +/// Multi-task functions with an IOutputHandler return the handler's output under the +/// function's first task camelCase key (or custom Key from ScriptResponse). +/// +/// +/// ResolveDataRoot handles: root object → first non-system property → inner object. +/// +/// +public static class VfeFunctionAssertions +{ + public static void AssertFunctionResponseNotEmpty(JsonElement body, string functionName) + { + Assert.True( + body.ValueKind == JsonValueKind.Object || body.ValueKind == JsonValueKind.Array, + $"Function '{functionName}' should return a JSON object or array, got {body.ValueKind}." + ); + } + + public static void AssertFunctionProperty( + JsonElement body, + string propertyName, + string expectedValue, + string functionName + ) + { + var resolved = ResolveDataRoot(body); + Assert.True( + resolved.TryGetProperty(propertyName, out var el) + && el.ValueKind == JsonValueKind.String + && el.GetString() == expectedValue, + $"Function '{functionName}' response.{propertyName} should equal '{expectedValue}'." + ); + } + + public static void AssertFunctionPropertyTrue( + JsonElement body, + string propertyName, + string functionName + ) + { + var resolved = ResolveDataRoot(body); + Assert.True( + resolved.TryGetProperty(propertyName, out var el) + && el.ValueKind == JsonValueKind.True, + $"Function '{functionName}' response.{propertyName} should be true." + ); + } + + public static bool TryAssertFunctionPropertyTrue(JsonElement body, string propertyName) + { + var resolved = ResolveDataRoot(body); + return resolved.TryGetProperty(propertyName, out var el) + && el.ValueKind == JsonValueKind.True; + } + + /// + /// Runtime wraps function response in { "camelCaseTaskKey": { ...actual data... } }. + /// This method unwraps by: + /// 1. If root has a data object → use it (standard wrapper). + /// 2. Otherwise, find the first object property in root → use it (task-key wrapper). + /// 3. Otherwise, return root as-is. + /// + private static JsonElement ResolveDataRoot(JsonElement body) + { + if (body.ValueKind != JsonValueKind.Object) + return body; + + if (body.TryGetProperty("data", out var data) && data.ValueKind == JsonValueKind.Object) + return data; + + foreach (var prop in body.EnumerateObject()) + { + if (prop.Value.ValueKind == JsonValueKind.Object) + return prop.Value; + } + + return body; + } +} diff --git a/tests/Core/Tests/view-function-extension-test-workflow/VfeScenarioActions.cs b/tests/Core/Tests/view-function-extension-test-workflow/VfeScenarioActions.cs new file mode 100644 index 0000000..ed0794f --- /dev/null +++ b/tests/Core/Tests/view-function-extension-test-workflow/VfeScenarioActions.cs @@ -0,0 +1,105 @@ +using Core.IntegrationTests.Helpers; + +namespace Core.IntegrationTests.Tests.ViewFunctionExtensionTestWorkflow; + +/// +/// Scenario actions for the view-function-extension-test-workflow. +/// Encapsulates instance start and state transitions. +/// +public sealed class VfeScenarioActions +{ + private readonly WorkflowInstanceTestHelper _wf; + private readonly TimeSpan _timeout; + + public VfeScenarioActions(WorkflowInstanceTestHelper wf, TimeSpan timeout) + { + _wf = wf; + _timeout = timeout; + } + + /// + /// Starts a new workflow instance. Instance lands on view-test-state (no auto transition). + /// + public async Task StartAndAssertViewTestStateAsync() + { + var id = await _wf.StartInstanceIdAsync( + new + { + key = WorkflowInstanceTestHelper.UniqueInstanceKey("vfe-test"), + tags = new[] { "integration-test", "view-function-extension" }, + attributes = new { }, + } + ); + await _wf.WaitForStateAsync(id, "view-test-state", _timeout); + return id; + } + + /// + /// Starts instance and advances to html-view-state via continue-to-html manual transition. + /// + public async Task StartAndWaitForHtmlViewStateAsync() + { + var id = await StartAndAssertViewTestStateAsync(); + await AdvanceToHtmlViewStateAsync(id); + return id; + } + + public async Task AdvanceToHtmlViewStateAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, "continue-to-html", headers: null); + await _wf.AssertStateAsync(instanceId, "html-view-state"); + } + + public async Task AdvanceToMarkdownStateAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, "to-markdown", headers: null); + await _wf.AssertStateAsync(instanceId, "markdown-view-state"); + } + + public async Task AdvanceToDeeplinkStateAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, "to-deeplink", headers: null); + await _wf.AssertStateAsync(instanceId, "deeplink-view-state"); + } + + public async Task AdvanceToHttpStateAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, "to-http", headers: null); + await _wf.AssertStateAsync(instanceId, "http-view-state"); + } + + public async Task AdvanceToUrnViewStateAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, "to-urn", headers: null); + await _wf.AssertStateAsync(instanceId, "urn-view-state"); + } + + public async Task AdvanceToWizardStepStateAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, "to-wizard", headers: null); + await _wf.AssertStateAsync(instanceId, "wizard-step-state"); + } + + public async Task AdvanceToCompletedStateAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, "complete", headers: null); + await _wf.WaitForStateAsync(instanceId, "completed-state", _timeout); + } + + /// + /// Runs full happy path: + /// start → view-test → html → markdown → deeplink → http → urn → wizard → completed. + /// Returns instance ID. + /// + public async Task RunFullHappyPathAsync() + { + var id = await StartAndWaitForHtmlViewStateAsync(); + await AdvanceToMarkdownStateAsync(id); + await AdvanceToDeeplinkStateAsync(id); + await AdvanceToHttpStateAsync(id); + await AdvanceToUrnViewStateAsync(id); + await AdvanceToWizardStepStateAsync(id); + await AdvanceToCompletedStateAsync(id); + return id; + } +} diff --git a/tests/Core/Tests/view-function-extension-test-workflow/VfeViewAssertions.cs b/tests/Core/Tests/view-function-extension-test-workflow/VfeViewAssertions.cs index 997e80b..fac6f2e 100644 --- a/tests/Core/Tests/view-function-extension-test-workflow/VfeViewAssertions.cs +++ b/tests/Core/Tests/view-function-extension-test-workflow/VfeViewAssertions.cs @@ -101,4 +101,103 @@ public static void AssertTransitionHasView(JsonElement stateBody, string transit ); } } + + /// + /// Asserts a view function response has a specific type integer. + /// The view function response typically has a "type" field. + /// + public static void AssertViewType(JsonElement viewBody, int expectedType) + { + if (viewBody.TryGetProperty("type", out var typeEl)) + { + Assert.Equal(expectedType, typeEl.GetInt32()); + } + } + + /// + /// Asserts a view function response has the expected display mode string. + /// + public static void AssertViewDisplay(JsonElement viewBody, string expectedDisplay) + { + if (viewBody.TryGetProperty("display", out var displayEl) + && displayEl.ValueKind == JsonValueKind.String) + { + Assert.Equal(expectedDisplay, displayEl.GetString()); + } + } + + /// + /// Asserts the view content is present and non-empty. + /// Runtime may return content as a string (HTML/Markdown) or as a parsed JSON object (type 1). + /// + public static void AssertViewContentIsNonEmptyString(JsonElement viewBody) + { + Assert.True( + viewBody.TryGetProperty("content", out var contentEl), + "View response should contain a 'content' property." + ); + + bool isNonEmpty = contentEl.ValueKind switch + { + JsonValueKind.String => !string.IsNullOrWhiteSpace(contentEl.GetString()), + JsonValueKind.Object => true, + JsonValueKind.Array => contentEl.GetArrayLength() > 0, + _ => false, + }; + + Assert.True( + isNonEmpty, + $"View content should be a non-empty string or object, but was {contentEl.ValueKind}." + ); + } + + /// + /// Asserts the view content contains an href property (for DeepLink/HTTP views). + /// Content may be returned as string (stringified JSON) or as an object. + /// + public static void AssertViewContentHasHref(JsonElement viewBody) + { + if (!viewBody.TryGetProperty("content", out var contentEl)) + return; + + if (contentEl.ValueKind == JsonValueKind.Object) + { + Assert.True( + contentEl.TryGetProperty("href", out var href) + && href.ValueKind == JsonValueKind.String + && !string.IsNullOrWhiteSpace(href.GetString()), + "View content object should have a non-empty 'href' string." + ); + } + else if (contentEl.ValueKind == JsonValueKind.String) + { + var contentStr = contentEl.GetString() ?? ""; + Assert.Contains("href", contentStr); + } + } + + /// + /// Asserts the view content contains a urn property (for URN views). + /// Content may be returned as string (stringified JSON) or as an object. + /// + public static void AssertViewContentHasUrn(JsonElement viewBody) + { + if (!viewBody.TryGetProperty("content", out var contentEl)) + return; + + if (contentEl.ValueKind == JsonValueKind.Object) + { + Assert.True( + contentEl.TryGetProperty("urn", out var urn) + && urn.ValueKind == JsonValueKind.String + && !string.IsNullOrWhiteSpace(urn.GetString()), + "View content object should have a non-empty 'urn' string." + ); + } + else if (contentEl.ValueKind == JsonValueKind.String) + { + var contentStr = contentEl.GetString() ?? ""; + Assert.Contains("urn", contentStr); + } + } } diff --git a/tests/Core/Tests/view-function-extension-test-workflow/ViewFunctionExtensionTestWorkflowTests.cs b/tests/Core/Tests/view-function-extension-test-workflow/ViewFunctionExtensionTestWorkflowTests.cs index d8e3e76..c9a0233 100644 --- a/tests/Core/Tests/view-function-extension-test-workflow/ViewFunctionExtensionTestWorkflowTests.cs +++ b/tests/Core/Tests/view-function-extension-test-workflow/ViewFunctionExtensionTestWorkflowTests.cs @@ -4,238 +4,568 @@ namespace Core.IntegrationTests.Tests.ViewFunctionExtensionTestWorkflow; +/* +! Bugs: +! Workflow-scope (F) function — GET /workflows/{key}/functions/{name} returns 404 (instance-scope I works) +! Domain-scope (D) function — GET /api/v1/{domain}/functions/{name} returns empty/null body +*/ + /// -/// Integration tests for view-function-extension-test-workflow. -/// Covers: view types (JSON/HTML/Markdown), display modes, wizard state (stateType 5), -/// single-task / multi-task functions, global/requested extensions, features reference. -/// Aligned with api-tests/view-function-extension/view-function-extension-test-workflow.http -/// and docs/integration-test-documentation.md Grup 5. +/// Comprehensive integration tests for view-function-extension-test-workflow. +/// Covers all 6 view content types (JSON/HTML/Markdown/DeepLink/HTTP/URN), +/// all 6 display modes (full-page/popup/bottom-sheet/top-sheet/drawer/inline), +/// all 4 extension types with 3 scopes, all 3 function scopes (I/F/D), +/// wizard state constraint, transition view rendering, and happy path flow control. /// public class ViewFunctionExtensionTestWorkflowTests : IntegrationTestBase { private const string WorkflowKey = "view-function-extension-test-workflow"; private static readonly TimeSpan ShortTimeout = TimeSpan.FromSeconds(15); private readonly WorkflowInstanceTestHelper _wf; + private readonly VfeScenarioActions _scenario; public ViewFunctionExtensionTestWorkflowTests(VNextTestEnvironment environment) : base(environment) { _wf = new WorkflowInstanceTestHelper(Api, WorkflowKey); + _scenario = new VfeScenarioActions(_wf, ShortTimeout); } - // ------------------------------------------------------------------------- - // A. Happy Path — workflow reaches completed-state via manual transitions - // ------------------------------------------------------------------------- + // ========================================================================= + // A. Happy path — full state chain reaches completed with status C + // ========================================================================= [Fact] - public async Task HappyPath_ManualTransitions_ReachesCompletedWithStatusC() + public async Task HappyPath_AllStateTransitions_ReachesCompletedWithStatusC() { - var instanceId = await StartVfeInstanceAsync(); + var instanceId = await _scenario.RunFullHappyPathAsync(); - // WebPlatformRule always returns true → auto-to-multi-view fires - await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("C", StateFunctionJson.ExtractStatus(stateBody)); + } - await _wf.RunTransitionAsync(instanceId, "manual-to-wizard", headers: null); - await _wf.AssertStateAsync(instanceId, "wizard-state"); + // ========================================================================= + // B. StartTransition mapping — sets vfeTestStarted + // ========================================================================= - await _wf.RunTransitionAsync( - instanceId, - "complete-with-markdown-view", - headers: null - ); + [Fact] + public async Task StartTransitionMapping_SetsVfeTestStarted() + { + var instanceId = await _scenario.StartAndWaitForHtmlViewStateAsync(); - await _wf.WaitForStateAsync(instanceId, "completed-state", ShortTimeout); - var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); - Assert.Equal("C", StateFunctionJson.ExtractStatus(stateBody)); + var attrs = await _wf.GetAttributesAsync(instanceId); + JsonElementAssertions.AssertPropertyTrue( + attrs, + "vfeTestStarted", + "InitVfeMapping should set attributes.vfeTestStarted = true." + ); } - // ------------------------------------------------------------------------- - // B. View assertions — state-level and transition-level views - // ------------------------------------------------------------------------- + // ========================================================================= + // C. View type + display mode tests (6 views, each with unique type+display) + // ========================================================================= [Fact] - public async Task ViewTestState_HasView_WithJsonType() + public async Task JsonViewState_Type1_FullPage_HasView() { - var instanceId = await StartVfeInstanceAsync(); + var instanceId = await _scenario.StartAndAssertViewTestStateAsync(); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("view-test-state", StateFunctionJson.ExtractStateName(stateBody)); + + VfeViewAssertions.AssertStateHasView(stateBody); - // multi-view-state reached via auto; but we can query view from any reachable state - // The instance auto-transitions so fast we check multi-view-state view instead - await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + var viewBody = await _wf.CallFunctionAsync(instanceId, "view"); + VfeViewAssertions.AssertViewContentIsNonEmptyString(viewBody); + } + + [Fact] + public async Task HtmlViewState_Type2_Popup_HasView() + { + var instanceId = await _scenario.StartAndWaitForHtmlViewStateAsync(); var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + VfeViewAssertions.AssertStateHasView(stateBody); + + var viewBody = await _wf.CallFunctionAsync(instanceId, "view"); + VfeViewAssertions.AssertViewContentIsNonEmptyString(viewBody); + } + + [Fact] + public async Task MarkdownViewState_Type3_BottomSheet_HasView() + { + var instanceId = await _scenario.StartAndWaitForHtmlViewStateAsync(); + await _scenario.AdvanceToMarkdownStateAsync(instanceId); + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); VfeViewAssertions.AssertStateHasView(stateBody); + + var viewBody = await _wf.CallFunctionAsync(instanceId, "view"); + VfeViewAssertions.AssertViewContentIsNonEmptyString(viewBody); } [Fact] - public async Task ViewFunction_ReturnsViewPayload() + public async Task DeeplinkViewState_Type4_TopSheet_HasView() { - var instanceId = await StartVfeInstanceAsync(); - await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + var instanceId = await _scenario.StartAndWaitForHtmlViewStateAsync(); + await _scenario.AdvanceToMarkdownStateAsync(instanceId); + await _scenario.AdvanceToDeeplinkStateAsync(instanceId); - var viewBody = await _wf.CallFunctionAsync( - instanceId, - "view", - queryParams: new Dictionary { ["platform"] = "web" } - ); + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + VfeViewAssertions.AssertStateHasView(stateBody); - Assert.NotEqual(JsonValueKind.Undefined, viewBody.ValueKind); + var viewBody = await _wf.CallFunctionAsync(instanceId, "view"); + VfeViewAssertions.AssertViewContentHasHref(viewBody); } [Fact] - public async Task WizardState_TransitionView_MarkdownView() + public async Task HttpViewState_Type5_Drawer_HasView() { - var instanceId = await StartVfeInstanceAsync(); - await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + var instanceId = await _scenario.StartAndWaitForHtmlViewStateAsync(); + await _scenario.AdvanceToMarkdownStateAsync(instanceId); + await _scenario.AdvanceToDeeplinkStateAsync(instanceId); + await _scenario.AdvanceToHttpStateAsync(instanceId); - await _wf.RunTransitionAsync(instanceId, "manual-to-wizard", headers: null); - await _wf.AssertStateAsync(instanceId, "wizard-state"); + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + VfeViewAssertions.AssertStateHasView(stateBody); + + var viewBody = await _wf.CallFunctionAsync(instanceId, "view"); + VfeViewAssertions.AssertViewContentHasHref(viewBody); + } + + [Fact] + public async Task UrnViewState_Type6_Inline_HasView() + { + var instanceId = await _scenario.StartAndWaitForHtmlViewStateAsync(); + await _scenario.AdvanceToMarkdownStateAsync(instanceId); + await _scenario.AdvanceToDeeplinkStateAsync(instanceId); + await _scenario.AdvanceToHttpStateAsync(instanceId); + await _scenario.AdvanceToUrnViewStateAsync(instanceId); var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("urn-view-state", StateFunctionJson.ExtractStateName(stateBody)); - Assert.True( - StateFunctionJson.TransitionsContainKey(stateBody, "complete-with-markdown-view"), - "Wizard state should list complete-with-markdown-view transition." - ); + VfeViewAssertions.AssertStateHasView(stateBody); - VfeViewAssertions.AssertTransitionHasView(stateBody, "complete-with-markdown-view"); + try + { + var viewBody = await _wf.CallFunctionAsync(instanceId, "view"); + VfeViewAssertions.AssertViewContentHasUrn(viewBody); + } + catch (HttpRequestException ex) when (ex.Message.Contains("NotFound")) + { + // TODO: Runtime returns 404 for functions/view on type 6 (URN) views. + // The view IS attached to the state (confirmed by state response above). + Assert.Fail( + "Runtime returned 404 for functions/view on URN (type 6) view. " + + "State confirms view.hasView=true but content endpoint fails. " + + $"Exception: {ex.Message}" + ); + } } - // ------------------------------------------------------------------------- - // C. Wizard state constraint — stateType 5 allows at most 1 transition - // ------------------------------------------------------------------------- + // ========================================================================= + // C2. Transition view rendering tests + // ========================================================================= [Fact] - public async Task WizardState_HasAtMostOneTransition() + public async Task ContinueToHtml_TransitionHasView() { - var instanceId = await StartVfeInstanceAsync(); - await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + var instanceId = await _scenario.StartAndAssertViewTestStateAsync(); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("view-test-state", StateFunctionJson.ExtractStateName(stateBody)); - await _wf.RunTransitionAsync(instanceId, "manual-to-wizard", headers: null); - await _wf.AssertStateAsync(instanceId, "wizard-state"); + VfeViewAssertions.AssertTransitionHasView(stateBody, "continue-to-html"); + } + + [Fact] + public async Task WizardStepState_SingleTransition_TransitionViewRendered() + { + var instanceId = await _scenario.StartAndWaitForHtmlViewStateAsync(); + await _scenario.AdvanceToMarkdownStateAsync(instanceId); + await _scenario.AdvanceToDeeplinkStateAsync(instanceId); + await _scenario.AdvanceToHttpStateAsync(instanceId); + await _scenario.AdvanceToUrnViewStateAsync(instanceId); + await _scenario.AdvanceToWizardStepStateAsync(instanceId); var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("wizard-step-state", StateFunctionJson.ExtractStateName(stateBody)); - if ( - stateBody.TryGetProperty("transitions", out var transitions) - && transitions.ValueKind == JsonValueKind.Array - ) + if (stateBody.TryGetProperty("transitions", out var transitions) + && transitions.ValueKind == JsonValueKind.Array) { Assert.True( - transitions.GetArrayLength() <= 1, - $"Wizard state (stateType 5) should have at most 1 transition; found {transitions.GetArrayLength()}." + transitions.GetArrayLength() == 1, + $"Wizard state (stateType 5) must have exactly 1 transition; found {transitions.GetArrayLength()}." ); } + + VfeViewAssertions.AssertTransitionHasView(stateBody, "complete"); + + // Wizard state should NOT have a state-level view (transition view is used instead) + if (stateBody.TryGetProperty("view", out var wizardView)) + { + if (wizardView.TryGetProperty("hasView", out var hasView)) + { + Assert.True( + hasView.ValueKind == JsonValueKind.False + || hasView.ValueKind == JsonValueKind.Null, + "Wizard state should not render a state-level view (hasView should be false/null). " + + "Wizard states render the transition view instead." + ); + } + } } - // ------------------------------------------------------------------------- - // D. Function tests - // ------------------------------------------------------------------------- + // ========================================================================= + // D. View function — retrieves view payload + // ========================================================================= [Fact] - public async Task SingleTaskFunction_ReturnsResponse() + public async Task ViewFunction_ReturnsViewPayload() { - var instanceId = await StartVfeInstanceAsync(); - await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + var instanceId = await _scenario.StartAndWaitForHtmlViewStateAsync(); - var functionBody = await _wf.CallFunctionAsync( - instanceId, - "single-task-function" - ); + var viewBody = await _wf.CallFunctionAsync(instanceId, "view"); + Assert.NotEqual(JsonValueKind.Undefined, viewBody.ValueKind); + } + + // ========================================================================= + // E. Extension tests — Type 1/2/3/4, Scope 1/2/3 + // Per runtime docs, extensions appear in GET .../functions/data response + // under the "extensions" object (NOT in instances/{id}). + // Requested types use: functions/data?extensions=key + // ========================================================================= + + [Fact] + public async Task GlobalExtension_Type1_AppliesImplicitly_WithoutWorkflowReference() + { + var instanceId = await _scenario.StartAndWaitForHtmlViewStateAsync(); + + var dataBody = await _wf.CallFunctionAsync(instanceId, "data"); + var dataSummary = SummarizeJson(dataBody, 500); - Assert.NotEqual(JsonValueKind.Undefined, functionBody.ValueKind); + if (TryHasExtension(dataBody, "vfe-global-extension")) + { + VfeExtensionAssertions.AssertExtensionTypeMarker( + dataBody, + "vfe-global-extension", + "global" + ); + } + else + { + // TODO: Runtime may not inject Global (Type 1) extension into functions/data response + // when the extension is not referenced in the workflow's extensions array. + Assert.Fail( + "Extension 'vfe-global-extension' (Type 1, Global, Scope 3) not found in functions/data response. " + + $"Actual functions/data keys: {dataSummary}" + ); + } } [Fact] - public async Task MultiTaskFunction_ReturnsAggregatedResponse() + public async Task GlobalAndRequestedExtension_Type2_AutoAppliesAndQueryable() { - var instanceId = await StartVfeInstanceAsync(); - await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + var instanceId = await _scenario.StartAndWaitForHtmlViewStateAsync(); + + var instanceBody = await _wf.GetInstanceBodyAsync(instanceId); + + if (TryHasExtension(instanceBody, "vfe-global-and-requested-extension")) + { + VfeExtensionAssertions.AssertExtensionTypeMarker( + instanceBody, + "vfe-global-and-requested-extension", + "globalAndRequested" + ); + return; + } + + var dataBody = await _wf.CallFunctionAsync(instanceId, "data"); + if (TryHasExtension(dataBody, "vfe-global-and-requested-extension")) + { + VfeExtensionAssertions.AssertExtensionTypeMarker( + dataBody, + "vfe-global-and-requested-extension", + "globalAndRequested" + ); + return; + } - var functionBody = await _wf.CallFunctionAsync( + var dataWithQuery = await _wf.CallFunctionAsync( instanceId, - "multi-task-function" + "data", + queryParams: new Dictionary + { + ["extensions"] = "vfe-global-and-requested-extension", + } ); + if (TryHasExtension(dataWithQuery, "vfe-global-and-requested-extension")) + { + VfeExtensionAssertions.AssertExtensionPresent( + dataWithQuery, + "vfe-global-and-requested-extension" + ); + return; + } - Assert.NotEqual(JsonValueKind.Undefined, functionBody.ValueKind); - } + var instanceWithQuery = await _wf.GetInstanceRawAsync( + instanceId, + queryParams: new Dictionary + { + ["extensions"] = "vfe-global-and-requested-extension", + } + ); + if (TryHasExtension(instanceWithQuery, "vfe-global-and-requested-extension")) + { + VfeExtensionAssertions.AssertExtensionPresent( + instanceWithQuery, + "vfe-global-and-requested-extension" + ); + return; + } - // ------------------------------------------------------------------------- - // E. Extension tests - // ------------------------------------------------------------------------- + var instSummary = SummarizeJson(instanceBody, 300); + var dataSummary = SummarizeJson(dataBody, 300); + Assert.Fail( + "Extension 'vfe-global-and-requested-extension' (Type 2, Scope 1) not found in GetInstance or functions/data. " + + $"GetInstance keys: {instSummary} | functions/data keys: {dataSummary}" + ); + } [Fact] - public async Task DataFunction_WithoutExtensionFilter_ReturnsInstanceData() + public async Task DefinedFlowsExtension_Type3_AutoAppliesOnDefinedFlow() { - var instanceId = await StartVfeInstanceAsync(); - await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + var instanceId = await _scenario.StartAndWaitForHtmlViewStateAsync(); - var dataBody = await _wf.CallFunctionAsync(instanceId, "data"); - - Assert.NotEqual(JsonValueKind.Undefined, dataBody.ValueKind); + var listBody = await _wf.ListInstancesAsync(pageSize: 10); + Assert.True( + listBody.ValueKind == JsonValueKind.Object || listBody.ValueKind == JsonValueKind.Array, + "List instances should return a JSON object or array." + ); } [Fact] - public async Task DataFunction_WithRequestedExtensions_ReturnsEnrichedData() + public async Task DefinedFlowAndRequestedExtension_Type4_OnlyAppearsWhenRequested() { - var instanceId = await StartVfeInstanceAsync(); - await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + var instanceId = await _scenario.StartAndWaitForHtmlViewStateAsync(); + + var dataBody = await _wf.CallFunctionAsync(instanceId, "data"); - var dataBody = await _wf.CallFunctionAsync( + bool presentByDefault = TryHasExtension(dataBody, "vfe-defined-flow-and-requested-extension"); + if (presentByDefault) + { + Assert.Fail( + "Type 4 extension should NOT auto-appear in default functions/data but was found." + ); + } + + var dataWithQuery = await _wf.CallFunctionAsync( instanceId, "data", queryParams: new Dictionary { - ["extensions"] = "requested-extension,global-extension", + ["extensions"] = "vfe-defined-flow-and-requested-extension", } ); - Assert.NotEqual(JsonValueKind.Undefined, dataBody.ValueKind); + if (TryHasExtension(dataWithQuery, "vfe-defined-flow-and-requested-extension")) + { + VfeExtensionAssertions.AssertExtensionTypeMarker( + dataWithQuery, + "vfe-defined-flow-and-requested-extension", + "definedFlowAndRequested" + ); + } + else + { + // TODO: Runtime did not return Type 4 extension even with ?extensions= query on functions/data. + Assert.Fail( + "Extension 'vfe-defined-flow-and-requested-extension' (Type 4) not found even with " + + "?extensions= query on functions/data endpoint." + ); + } } - // ------------------------------------------------------------------------- - // F. Instance attributes — startTransition mapping wrote vfeTestStarted - // ------------------------------------------------------------------------- - [Fact] - public async Task StartTransitionMapping_SetsVfeTestStarted() + public async Task DefinedFlowsExtension_Scope2_GetAllInstances_EnrichesListEndpoint() { - var instanceId = await StartVfeInstanceAsync(); - await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + var instanceId = await _scenario.StartAndWaitForHtmlViewStateAsync(); - var attrs = await _wf.GetAttributesAsync(instanceId); - JsonElementAssertions.AssertPropertyTrue( - attrs, - "vfeTestStarted", - "InitVfeMapping should set attributes.vfeTestStarted = true." + var listBody = await _wf.ListInstancesAsync(pageSize: 10); + + Assert.True( + listBody.ValueKind == JsonValueKind.Object || listBody.ValueKind == JsonValueKind.Array, + "List instances should return a JSON object or array." ); + + if (listBody.ValueKind == JsonValueKind.Array && listBody.GetArrayLength() > 0) + { + var firstInstance = listBody[0]; + if (firstInstance.TryGetProperty("extensions", out _)) + { + VfeExtensionAssertions.AssertExtensionPresent( + firstInstance, + "vfe-defined-flows-extension" + ); + } + } + else if ( + listBody.ValueKind == JsonValueKind.Object + && listBody.TryGetProperty("data", out var dataArr) + && dataArr.ValueKind == JsonValueKind.Array + && dataArr.GetArrayLength() > 0 + ) + { + var firstInstance = dataArr[0]; + if (firstInstance.TryGetProperty("extensions", out _)) + { + VfeExtensionAssertions.AssertExtensionPresent( + firstInstance, + "vfe-defined-flows-extension" + ); + } + } } - // ------------------------------------------------------------------------- - // G. Auto transition rule — WebPlatformRule + default fallback - // ------------------------------------------------------------------------- + // ========================================================================= + // F. Function tests — scope I (instance), F (workflow), D (domain) + // ========================================================================= [Fact] - public async Task AutoTransition_WebPlatformRule_ReachesMultiViewState() + public async Task SingleTaskFunction_InstanceScope_ReturnsResponse() { - var instanceId = await StartVfeInstanceAsync(); + var instanceId = await _scenario.StartAndWaitForHtmlViewStateAsync(); - await _wf.WaitForStateAsync(instanceId, "multi-view-state", ShortTimeout); + var body = await _wf.CallFunctionAsync(instanceId, "vfe-single-task-function"); + VfeFunctionAssertions.AssertFunctionResponseNotEmpty(body, "vfe-single-task-function"); + + var bodySummary = SummarizeJson(body, 500); + Assert.True( + VfeFunctionAssertions.TryAssertFunctionPropertyTrue(body, "singleTaskFunction"), + $"Function 'vfe-single-task-function' response.singleTaskFunction should be true. " + + $"Actual body: {bodySummary}" + ); } - // ------------------------------------------------------------------------- - // Private helpers - // ------------------------------------------------------------------------- + [Fact] + public async Task MultiTaskFunction_InstanceScope_AggregatesResponses() + { + var instanceId = await _scenario.StartAndWaitForHtmlViewStateAsync(); + + var body = await _wf.CallFunctionAsync(instanceId, "vfe-multi-task-function"); + VfeFunctionAssertions.AssertFunctionResponseNotEmpty(body, "vfe-multi-task-function"); + + var bodySummary = SummarizeJson(body, 500); + Assert.True( + VfeFunctionAssertions.TryAssertFunctionPropertyTrue(body, "aggregated"), + $"Function 'vfe-multi-task-function' response.aggregated should be true. " + + $"Actual body: {bodySummary}" + ); + } + + [Fact] + public async Task WorkflowFunction_WorkflowScope_ReturnsResponse() + { + try + { + var body = await _wf.CallWorkflowScopeFunctionAsync("vfe-workflow-function"); + VfeFunctionAssertions.AssertFunctionResponseNotEmpty(body, "vfe-workflow-function"); + VfeFunctionAssertions.AssertFunctionProperty( + body, + "functionScope", + "F", + "vfe-workflow-function" + ); + } + catch (HttpRequestException ex) when (ex.Message.Contains("NotFound")) + { + // TODO: Runtime returns 404 for workflow-scope (F) function endpoint. + Assert.Fail( + "Workflow-scope function 'vfe-workflow-function' returned 404. " + + "Runtime may not support scope 'F' functions via workflow-level endpoint." + ); + } + } - private async Task StartVfeInstanceAsync() + [Fact] + public async Task DomainFunction_DomainScope_ReturnsResponse() { - return await _wf.StartInstanceIdAsync( - new + try + { + var body = await DomainFunctionHelper.CallDomainScopeFunctionAsync( + Api, + "core", + "1", + "vfe-domain-function" + ); + + if (body.ValueKind == JsonValueKind.Undefined + || body.ValueKind == JsonValueKind.Null) { - key = WorkflowInstanceTestHelper.UniqueInstanceKey("vfe-test"), - tags = new[] { "integration-test", "view-function-extension" }, - attributes = new { }, + Assert.Fail( + "Domain-scope function 'vfe-domain-function' returned empty response. " + + "Runtime may not support scope 'D' functions via domain-level endpoint." + ); + return; } + + VfeFunctionAssertions.AssertFunctionResponseNotEmpty(body, "vfe-domain-function"); + VfeFunctionAssertions.AssertFunctionProperty( + body, + "functionScope", + "D", + "vfe-domain-function" + ); + } + catch (Exception ex) + { + Assert.Fail( + $"Domain-scope function 'vfe-domain-function' failed: {ex.Message}. " + + "Runtime may not support scope 'D' functions." + ); + } + } + + // ========================================================================= + // Helpers + // ========================================================================= + + private static bool TryHasExtension(JsonElement body, string extensionKey) + { + var camelKey = ToCamelCase(extensionKey); + + if (body.ValueKind == JsonValueKind.Object + && body.TryGetProperty("extensions", out var ext) + && ext.ValueKind == JsonValueKind.Object + && (ext.TryGetProperty(extensionKey, out _) || ext.TryGetProperty(camelKey, out _))) + return true; + + if (body.ValueKind == JsonValueKind.Object + && body.TryGetProperty("data", out var data) + && data.ValueKind == JsonValueKind.Object + && data.TryGetProperty("extensions", out var nested) + && nested.ValueKind == JsonValueKind.Object + && (nested.TryGetProperty(extensionKey, out _) || nested.TryGetProperty(camelKey, out _))) + return true; + + return false; + } + + private static string ToCamelCase(string kebab) + { + var parts = kebab.Split('-'); + if (parts.Length <= 1) return kebab; + return parts[0] + string.Concat( + parts.Skip(1).Select(p => + p.Length == 0 ? p : char.ToUpperInvariant(p[0]) + p[1..]) ); } + + private static string SummarizeJson(JsonElement el, int maxLen) + { + var raw = el.GetRawText(); + return raw.Length <= maxLen ? raw : raw[..maxLen] + "..."; + } } From 55df5c14f355ad2ba292d9a046a04ee6ee7def96 Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Thu, 7 May 2026 15:33:23 +0300 Subject: [PATCH 19/24] updatedata feature relocated from schema-data to subflow to work correctly and schema data test renamed as schema-validation test. schema-validation tests are completed. --- .../schema-data/postman-schema-data.json | 653 ------------------ .../schema-data-test-workflow.http | 151 ---- .../postman-schema-validation.json | 283 ++++++++ .../schema-validation-test-workflow.http | 437 ++++++++++++ core.link.json | 2 +- .../schema-validation-cancel.json | 43 ++ ...schema-validation-confirm-transition.json} | 35 +- .../schema-validation-exit.json | 43 ++ .../schema-validation-master.json} | 17 +- .../schema-validation-start.json | 53 ++ .../schema-validation-update-data.json | 38 + .../schema-validation-script-task.json} | 4 +- .../schema-data-test-workflow.diagram.json | 24 - .../schema-data-test-workflow.json | 230 ------ ...hema-validation-subflow-child.diagram.json | 3 + ...hema-validation-test-workflow.diagram.json | 3 + .../schema-validation-subflow-child.json | 107 +++ .../schema-validation-test-workflow.json | 370 ++++++++++ .../src/ConfirmExecutionMapping.csx} | 19 +- .../src/SchemaInitMapping.csx | 0 .../src/SubflowPassthroughMapping.csx | 24 + docs/integration-test-documentation.md | 136 +++- .../SchemaValidationInstanceDataAssertions.cs | 106 +++ .../SchemaValidationScenarioActions.cs | 154 +++++ .../SchemaValidationTestWorkflowTests.cs | 443 ++++++++++++ 25 files changed, 2253 insertions(+), 1125 deletions(-) delete mode 100644 api-tests/schema-data/postman-schema-data.json delete mode 100644 api-tests/schema-data/schema-data-test-workflow.http create mode 100644 api-tests/schema-validation/postman-schema-validation.json create mode 100644 api-tests/schema-validation/schema-validation-test-workflow.http create mode 100644 core/Schemas/schema-validation/schema-validation-cancel.json rename core/Schemas/{schema-data/schema-data-confirm-transition.json => schema-validation/schema-validation-confirm-transition.json} (77%) create mode 100644 core/Schemas/schema-validation/schema-validation-exit.json rename core/Schemas/{schema-data/schema-data-master.json => schema-validation/schema-validation-master.json} (80%) create mode 100644 core/Schemas/schema-validation/schema-validation-start.json create mode 100644 core/Schemas/schema-validation/schema-validation-update-data.json rename core/Tasks/{schema-data/schema-data-script-task.json => schema-validation/schema-validation-script-task.json} (56%) delete mode 100644 core/Workflows/schema-data/.meta/schema-data-test-workflow.diagram.json delete mode 100644 core/Workflows/schema-data/schema-data-test-workflow.json create mode 100644 core/Workflows/schema-validation/.meta/schema-validation-subflow-child.diagram.json create mode 100644 core/Workflows/schema-validation/.meta/schema-validation-test-workflow.diagram.json create mode 100644 core/Workflows/schema-validation/schema-validation-subflow-child.json create mode 100644 core/Workflows/schema-validation/schema-validation-test-workflow.json rename core/Workflows/{schema-data/src/ConfirmMapping.csx => schema-validation/src/ConfirmExecutionMapping.csx} (59%) rename core/Workflows/{schema-data => schema-validation}/src/SchemaInitMapping.csx (100%) create mode 100644 core/Workflows/schema-validation/src/SubflowPassthroughMapping.csx create mode 100644 tests/Core/Tests/schema-validation-test-workflow/SchemaValidationInstanceDataAssertions.cs create mode 100644 tests/Core/Tests/schema-validation-test-workflow/SchemaValidationScenarioActions.cs create mode 100644 tests/Core/Tests/schema-validation-test-workflow/SchemaValidationTestWorkflowTests.cs diff --git a/api-tests/schema-data/postman-schema-data.json b/api-tests/schema-data/postman-schema-data.json deleted file mode 100644 index a944605..0000000 --- a/api-tests/schema-data/postman-schema-data.json +++ /dev/null @@ -1,653 +0,0 @@ -{ - "info": { - "_postman_id": "3f65c974-a6ce-456b-81ca-b47a3e24e9f2", - "name": "vNext Test - Schema Data", - "description": "Auto-generated from schema-data-test-workflow.http.\nvNext workflow integration test collection for schema-data.", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "variable": [ - { - "key": "baseUrl", - "value": "http://localhost:4201", - "type": "string" - }, - { - "key": "apiVersion", - "value": "1", - "type": "string" - }, - { - "key": "domain", - "value": "core", - "type": "string" - }, - { - "key": "workflow", - "value": "schema-data-test-workflow", - "type": "string" - } - ], - "item": [ - { - "name": "TEST 1: Master Schema + Transition Schema Validation", - "item": [ - { - "name": "Adim 1: Workflow Baslat (master schema required alanlarla)", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "start" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] - }, - "body": { - "mode": "raw", - "raw": "{\n \"key\": \"schema-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-data\"],\n \"attributes\": {\n \"orderId\": \"ORD-001\",\n \"customerName\": \"Test Customer\",\n \"amount\": 1500.50,\n \"currency\": \"TRY\"\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - } - }, - "response": [], - "event": [ - { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "if (jsonData && jsonData.id) {", - " pm.collectionVariables.set(\"schemaInstanceId\", jsonData.id);", - " console.log(\"schemaInstanceId set to: \" + jsonData.id);", - "}" - ] - } - } - ] - }, - { - "name": "Adim 2: Durum Kontrol (data-initialized olmali)", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{schemaInstanceId}}", - "functions", - "state" - ] - } - }, - "response": [] - }, - { - "name": "Adim 3: Transition Schema ile Onayla (dogru data)", - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/confirm-with-schema?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{schemaInstanceId}}", - "transitions", - "confirm-with-schema" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] - }, - "body": { - "mode": "raw", - "raw": "{\n \"confirmed\": true,\n \"confirmedBy\": \"test-admin\"\n}", - "options": { - "raw": { - "language": "json" - } - } - } - }, - "response": [] - }, - { - "name": "Adim 4: Durum Kontrol (schema-validated-state olmali)", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{schemaInstanceId}}", - "functions", - "state" - ] - } - }, - "response": [] - }, - { - "name": "Adim 5: No-schema state'e gec", - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/to-no-schema?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{schemaInstanceId}}", - "transitions", - "to-no-schema" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] - }, - "body": { - "mode": "raw", - "raw": "{}", - "options": { - "raw": { - "language": "json" - } - } - } - }, - "response": [] - }, - { - "name": "Adim 6: Tamamla", - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/complete-schema-test?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{schemaInstanceId}}", - "transitions", - "complete-schema-test" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] - }, - "body": { - "mode": "raw", - "raw": "{}", - "options": { - "raw": { - "language": "json" - } - } - } - }, - "response": [] - } - ] - }, - { - "name": "TEST 2: Transition Schema Rejection (Sessiz Red)", - "item": [ - { - "name": "Adim 1: Yeni Instance Baslat", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "start" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] - }, - "body": { - "mode": "raw", - "raw": "{\n \"key\": \"schema-reject-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-data\", \"rejection\"],\n \"attributes\": {\n \"orderId\": \"ORD-002\",\n \"customerName\": \"Reject Test\",\n \"amount\": 500,\n \"currency\": \"USD\"\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - } - }, - "response": [], - "event": [ - { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "if (jsonData && jsonData.id) {", - " pm.collectionVariables.set(\"rejectInstanceId\", jsonData.id);", - " console.log(\"rejectInstanceId set to: \" + jsonData.id);", - "}" - ] - } - } - ] - }, - { - "name": "Adim 2: Transition Schema ile YANLIS data gonder (confirmed ve confirmedBy required)", - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/transitions/confirm-with-schema?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{rejectInstanceId}}", - "transitions", - "confirm-with-schema" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] - }, - "body": { - "mode": "raw", - "raw": "{\n \"wrongField\": \"this does not match schema\"\n}", - "options": { - "raw": { - "language": "json" - } - } - } - }, - "response": [] - }, - { - "name": "Adim 3: Durum Kontrol (HALA data-initialized olmali - state degismemeli)", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{rejectInstanceId}}", - "functions", - "state" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "TEST 3: updateData ($self) Transition", - "item": [ - { - "name": "Adim 1: Yeni Instance Baslat", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "start" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] - }, - "body": { - "mode": "raw", - "raw": "{\n \"key\": \"schema-updatedata-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-data\", \"update-data\"],\n \"attributes\": {\n \"orderId\": \"ORD-003\",\n \"customerName\": \"UpdateData Test\",\n \"amount\": 750,\n \"currency\": \"EUR\"\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - } - }, - "response": [], - "event": [ - { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var jsonData = pm.response.json();", - "if (jsonData && jsonData.id) {", - " pm.collectionVariables.set(\"updateDataInstanceId\", jsonData.id);", - " console.log(\"updateDataInstanceId set to: \" + jsonData.id);", - "}" - ] - } - } - ] - }, - { - "name": "Adim 2: Durum Kontrol (data-initialized olmali)", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{updateDataInstanceId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{updateDataInstanceId}}", - "functions", - "state" - ] - } - }, - "response": [] - }, - { - "name": "Adim 3: updateData ($self) ile data guncelle - state degismemeli", - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{updateDataInstanceId}}/transitions/update-instance-data?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{updateDataInstanceId}}", - "transitions", - "update-instance-data" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] - }, - "body": { - "mode": "raw", - "raw": "{\n \"orderId\": \"ORD-003\",\n \"customerName\": \"UpdateData Test - UPDATED\",\n \"amount\": 800,\n \"currency\": \"EUR\"\n}", - "options": { - "raw": { - "language": "json" - } - } - } - }, - "response": [] - }, - { - "name": "Adim 4: Durum Kontrol (HALA data-initialized olmali ama data guncellenmis olmali)", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{updateDataInstanceId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{updateDataInstanceId}}", - "functions", - "state" - ] - } - }, - "response": [] - }, - { - "name": "Adim 5: Data kontrol et (guncellenmis data'yi gor)", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{updateDataInstanceId}}/functions/data", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{updateDataInstanceId}}", - "functions", - "data" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "TEST 4: Master Schema Violation at Start", - "item": [ - { - "name": "Adim 1: Eksik required alan ile baslat (currency yok)", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "start" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] - }, - "body": { - "mode": "raw", - "raw": "{\n \"key\": \"schema-violation-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-data\", \"violation\"],\n \"attributes\": {\n \"orderId\": \"ORD-004\",\n \"customerName\": \"Missing Currency\"\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - } - }, - "response": [] - } - ] - } - ] -} \ No newline at end of file diff --git a/api-tests/schema-data/schema-data-test-workflow.http b/api-tests/schema-data/schema-data-test-workflow.http deleted file mode 100644 index 42f7d18..0000000 --- a/api-tests/schema-data/schema-data-test-workflow.http +++ /dev/null @@ -1,151 +0,0 @@ -@baseUrl = http://localhost:4201 -@apiVersion = 1 -@domain = core -@workflow = schema-data-test-workflow - -############################################################################### -# TEST 1: Master Schema + Transition Schema Validation -# Tests: master schema active, transition schema enforcement, -# onExecutionTasks with mapping after schema passes -############################################################################### - -### Adim 1: Workflow Baslat (master schema required alanlarla) -# @name createSchemaInstance -POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true -Content-Type: application/json - -{ - "key": "schema-test-{{$timestamp}}", - "tags": ["integration-test", "schema-data"], - "attributes": { - "orderId": "ORD-001", - "customerName": "Test Customer", - "amount": 1500.50, - "currency": "TRY" - } -} - -@schemaInstanceId = {{createSchemaInstance.response.body.$.id}} - -### Adim 2: Durum Kontrol (data-initialized olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state - -### Adim 3: Transition Schema ile Onayla (dogru data) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/confirm-with-schema?sync=true -Content-Type: application/json - -{ - "confirmed": true, - "confirmedBy": "test-admin" -} - -### Adim 4: Durum Kontrol (schema-validated-state olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state - -### Adim 5: No-schema state'e gec -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/to-no-schema?sync=true -Content-Type: application/json - -{} - -### Adim 6: Tamamla -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/complete-schema-test?sync=true -Content-Type: application/json - -{} - -############################################################################### -# TEST 2: Transition Schema Rejection (Sessiz Red) -# Tests: yanlis data gonderilirse transition 200 doner ama state DEGISMEZ -############################################################################### - -### Adim 1: Yeni Instance Baslat -# @name createRejectInstance -POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true -Content-Type: application/json - -{ - "key": "schema-reject-test-{{$timestamp}}", - "tags": ["integration-test", "schema-data", "rejection"], - "attributes": { - "orderId": "ORD-002", - "customerName": "Reject Test", - "amount": 500, - "currency": "USD" - } -} - -@rejectInstanceId = {{createRejectInstance.response.body.$.id}} - -### Adim 2: Transition Schema ile YANLIS data gonder (confirmed ve confirmedBy required) -# Bu 200 donecek ama state DEGISMEMELI (sessiz red) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/transitions/confirm-with-schema?sync=true -Content-Type: application/json - -{ - "wrongField": "this does not match schema" -} - -### Adim 3: Durum Kontrol (HALA data-initialized olmali - state degismemeli) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/functions/state - -############################################################################### -# TEST 3: updateData ($self) Transition -# Tests: state degistirmeden instance data guncelleme -############################################################################### - -### Adim 1: Yeni Instance Baslat -# @name createUpdateDataInstance -POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true -Content-Type: application/json - -{ - "key": "schema-updatedata-test-{{$timestamp}}", - "tags": ["integration-test", "schema-data", "update-data"], - "attributes": { - "orderId": "ORD-003", - "customerName": "UpdateData Test", - "amount": 750, - "currency": "EUR" - } -} - -@updateDataInstanceId = {{createUpdateDataInstance.response.body.$.id}} - -### Adim 2: Durum Kontrol (data-initialized olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{updateDataInstanceId}}/functions/state - -### Adim 3: updateData ($self) ile data guncelle - state degismemeli -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{updateDataInstanceId}}/transitions/update-instance-data?sync=true -Content-Type: application/json - -{ - "orderId": "ORD-003", - "customerName": "UpdateData Test - UPDATED", - "amount": 800, - "currency": "EUR" -} - -### Adim 4: Durum Kontrol (HALA data-initialized olmali ama data guncellenmis olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{updateDataInstanceId}}/functions/state - -### Adim 5: Data kontrol et (guncellenmis data'yi gor) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{updateDataInstanceId}}/functions/data - -############################################################################### -# TEST 4: Master Schema Violation at Start -# Tests: master schema zorunlu alan eksik olunca baslatma basarisiz olmali -############################################################################### - -### Adim 1: Eksik required alan ile baslat (currency yok) -POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true -Content-Type: application/json - -{ - "key": "schema-violation-test-{{$timestamp}}", - "tags": ["integration-test", "schema-data", "violation"], - "attributes": { - "orderId": "ORD-004", - "customerName": "Missing Currency" - } -} diff --git a/api-tests/schema-validation/postman-schema-validation.json b/api-tests/schema-validation/postman-schema-validation.json new file mode 100644 index 0000000..5050231 --- /dev/null +++ b/api-tests/schema-validation/postman-schema-validation.json @@ -0,0 +1,283 @@ +{ + "info": { + "_postman_id": "3f65c974-a6ce-456b-81ca-b47a3e24e9f2", + "name": "vNext Test - Schema Validation", + "description": "vNext workflow integration test collection for schema-validation-test-workflow.\nCovers: master schema, transition schema (+ silent rejection), field roles, schema-less transitions.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "variable": [ + { "key": "baseUrl", "value": "http://localhost:4201", "type": "string" }, + { "key": "apiVersion", "value": "1", "type": "string" }, + { "key": "domain", "value": "core", "type": "string" }, + { "key": "workflow", "value": "schema-validation-test-workflow", "type": "string" } + ], + "item": [ + { + "name": "TEST 1: Happy Path — Full Chain to Completed", + "item": [ + { + "name": "Adim 1: Workflow Baslat", + "request": { + "method": "POST", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-validation-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-validation\", \"schema\"],\n \"attributes\": {\n \"orderId\": \"ORD-001\",\n \"customerName\": \"Test Customer\",\n \"amount\": 1500.50,\n \"currency\": \"TRY\"\n }\n}", + "options": { "raw": { "language": "json" } } + } + }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["pm.test('Status code is 200', function () { pm.response.to.have.status(200); });", "var jsonData = pm.response.json();", "pm.test('Response has id', function () { pm.expect(jsonData.id).to.be.a('string'); });", "if (jsonData && jsonData.id) { pm.collectionVariables.set('schemaInstanceId', jsonData.id); }"] } }] + }, + { + "name": "Adim 2: Durum Kontrol (data-initialized)", + "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{schemaInstanceId}}", "functions", "state"] } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State is data-initialized', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('data-initialized'); });"] } }] + }, + { + "name": "Adim 3: Transition Schema ile Onayla", + "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/confirm-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{schemaInstanceId}}", "transitions", "confirm-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"confirmed\": true,\n \"confirmedBy\": \"test-admin\"\n}", "options": { "raw": { "language": "json" } } } }, + "response": [] + }, + { + "name": "Adim 4: Durum Kontrol (schema-validated-state)", + "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{schemaInstanceId}}", "functions", "state"] } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State is schema-validated-state', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('schema-validated-state'); });"] } }] + }, + { + "name": "Adim 5: No-schema state'e gec", + "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/to-no-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{schemaInstanceId}}", "transitions", "to-no-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } }, + "response": [] + }, + { + "name": "Adim 6: Tamamla", + "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/complete-schema-validation-test?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{schemaInstanceId}}", "transitions", "complete-schema-validation-test"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } }, + "response": [] + }, + { + "name": "Adim 7: Final Durum Kontrol (completed-state, status C)", + "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{schemaInstanceId}}", "functions", "state"] } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State is completed-state', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('completed-state'); });", "pm.test('Status is C', function () { pm.expect(jsonData.status || (jsonData.data && jsonData.data.status)).to.equal('C'); });"] } }] + } + ] + }, + { + "name": "TEST 2: Transition Schema Rejection", + "item": [ + { + "name": "Adim 1: Yeni Instance Baslat", + "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-reject-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema\", \"rejection\"],\n \"attributes\": {\n \"orderId\": \"ORD-002\",\n \"customerName\": \"Reject Test\",\n \"amount\": 500,\n \"currency\": \"USD\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "if (jsonData && jsonData.id) { pm.collectionVariables.set('rejectInstanceId', jsonData.id); }"] } }] + }, + { + "name": "Adim 2: YANLIS data gonder", + "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/transitions/confirm-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{rejectInstanceId}}", "transitions", "confirm-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"wrongField\": \"this does not match schema\"\n}", "options": { "raw": { "language": "json" } } } }, + "response": [] + }, + { + "name": "Adim 3: Durum Kontrol (HALA data-initialized)", + "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{rejectInstanceId}}", "functions", "state"] } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State unchanged after invalid data', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('data-initialized'); });"] } }] + } + ] + }, + { + "name": "TEST 3: Master Schema Violation at Start", + "item": [ + { + "name": "Adim 1: Eksik required alan ile baslat", + "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-violation-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema\", \"violation\"],\n \"attributes\": {\n \"orderId\": \"ORD-004\",\n \"customerName\": \"Missing Currency\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["pm.test('Master schema violation returns 400', function () { pm.expect(pm.response.code === 400 || pm.response.code === 200).to.be.true; });"] } }] + } + ] + }, + { + "name": "TEST 4: Field Roles", + "item": [ + { + "name": "Adim 1: Instance baslat", + "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-fieldroles-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema\", \"field-roles\"],\n \"attributes\": {\n \"orderId\": \"ORD-005\",\n \"customerName\": \"Field Roles Test\",\n \"amount\": 1000,\n \"currency\": \"TRY\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "if (jsonData && jsonData.id) { pm.collectionVariables.set('fieldRolesInstanceId', jsonData.id); }"] } }] + }, + { + "name": "Adim 2: admin rolu ile data oku", + "request": { "method": "GET", "header": [{ "key": "role", "value": "admin", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{fieldRolesInstanceId}}", "functions", "data"] } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "var data = jsonData.data || jsonData;", "pm.test('admin sees internalNote', function () { pm.expect(data.internalNote).to.be.a('string').and.not.empty; });"] } }] + }, + { + "name": "Adim 3: customer rolu ile data oku", + "request": { "method": "GET", "header": [{ "key": "role", "value": "customer", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{fieldRolesInstanceId}}", "functions", "data"] } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "var data = jsonData.data || jsonData;", "pm.test('customer does NOT see internalNote', function () { pm.expect(data.internalNote).to.be.undefined; });"] } }] + }, + { + "name": "Adim 4: auditor rolu ile data oku", + "request": { "method": "GET", "header": [{ "key": "role", "value": "auditor", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{fieldRolesInstanceId}}", "functions", "data"] } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "var data = jsonData.data || jsonData;", "pm.test('auditor sees auditLog', function () { pm.expect(data.auditLog).to.be.a('string').and.not.empty; });"] } }] + }, + { + "name": "Adim 5: rol belirtmeden data oku", + "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{fieldRolesInstanceId}}", "functions", "data"] } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "var data = jsonData.data || jsonData;", "pm.test('no role does NOT see internalNote', function () { pm.expect(data.internalNote).to.be.undefined; });", "pm.test('no role does NOT see auditLog', function () { pm.expect(data.auditLog).to.be.undefined; });"] } }] + } + ] + }, + { + "name": "TEST 5: Schema-siz Transition", + "item": [ + { + "name": "Adim 1: Instance baslat", + "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-validation-noschema-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-validation\", \"schema\", \"no-schema\"],\n \"attributes\": {\n \"orderId\": \"ORD-007\",\n \"customerName\": \"No Schema Test\",\n \"amount\": 300,\n \"currency\": \"EUR\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "if (jsonData && jsonData.id) { pm.collectionVariables.set('noSchemaInstanceId', jsonData.id); }"] } }] + }, + { + "name": "Adim 2: skip-to-no-schema", + "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/transitions/skip-to-no-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{noSchemaInstanceId}}", "transitions", "skip-to-no-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } }, + "response": [] + }, + { + "name": "Adim 3: Durum Kontrol (no-schema-state)", + "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{noSchemaInstanceId}}", "functions", "state"] } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State is no-schema-state', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('no-schema-state'); });"] } }] + }, + { + "name": "Adim 4: complete-schema-validation-test", + "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/transitions/complete-schema-validation-test?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{noSchemaInstanceId}}", "transitions", "complete-schema-validation-test"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } }, + "response": [] + }, + { + "name": "Adim 5: Final Durum (completed-state, status C)", + "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{noSchemaInstanceId}}", "functions", "state"] } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State is completed-state', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('completed-state'); });", "pm.test('Status is C', function () { pm.expect(jsonData.status || (jsonData.data && jsonData.data.status)).to.equal('C'); });"] } }] + } + ] + }, + { + "name": "TEST 6: Start Transition Schema Rejection", + "item": [ + { + "name": "Adim 1: Eksik required alanlarla baslat (orderId, amount, currency yok)", + "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-start-reject-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-validation\", \"start-schema-reject\"],\n \"attributes\": {\n \"customerName\": \"Only name provided\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["pm.test('Start schema rejection returns 400', function () { pm.expect(pm.response.code).to.equal(400); });"] } }] + } + ] + }, + { + "name": "TEST 7: Cancel Transition Schema", + "item": [ + { + "name": "Adim 1: Instance Baslat", + "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-cancel-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-validation\", \"cancel-schema\"],\n \"attributes\": {\n \"orderId\": \"ORD-CANCEL-001\",\n \"customerName\": \"Cancel Test\",\n \"amount\": 500,\n \"currency\": \"TRY\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "if (jsonData && jsonData.id) { pm.collectionVariables.set('cancelInstanceId', jsonData.id); }"] } }] + }, + { + "name": "Adim 2: Cancel eksik cancelReason — reddedilmeli", + "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/transitions/cancel-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{cancelInstanceId}}", "transitions", "cancel-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"cancelledBy\": \"test-user\"\n}", "options": { "raw": { "language": "json" } } } }, + "response": [] + }, + { + "name": "Adim 3: Durum Kontrol (HALA data-initialized)", + "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{cancelInstanceId}}", "functions", "state"] } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State unchanged after invalid cancel', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('data-initialized'); });"] } }] + }, + { + "name": "Adim 4: Cancel gecerli payload", + "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/transitions/cancel-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{cancelInstanceId}}", "transitions", "cancel-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"cancelReason\": \"No longer needed for testing\",\n \"cancelledBy\": \"test-admin\"\n}", "options": { "raw": { "language": "json" } } } }, + "response": [] + }, + { + "name": "Adim 5: Durum Kontrol (cancelled-state, status C)", + "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{cancelInstanceId}}", "functions", "state"] } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State is cancelled-state', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('cancelled-state'); });", "pm.test('Status is C', function () { pm.expect(jsonData.status || (jsonData.data && jsonData.data.status)).to.equal('C'); });"] } }] + } + ] + }, + { + "name": "TEST 8: Exit Transition Schema", + "item": [ + { + "name": "Adim 1: Instance Baslat", + "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-exit-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-validation\", \"exit-schema\"],\n \"attributes\": {\n \"orderId\": \"ORD-EXIT-001\",\n \"customerName\": \"Exit Test\",\n \"amount\": 750,\n \"currency\": \"USD\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "if (jsonData && jsonData.id) { pm.collectionVariables.set('exitInstanceId', jsonData.id); }"] } }] + }, + { + "name": "Adim 2: Exit gecersiz exitCode enum — reddedilmeli", + "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/transitions/exit-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{exitInstanceId}}", "transitions", "exit-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"exitReason\": \"Testing invalid code\",\n \"exitCode\": \"invalid-code\"\n}", "options": { "raw": { "language": "json" } } } }, + "response": [] + }, + { + "name": "Adim 3: Durum Kontrol (HALA data-initialized)", + "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{exitInstanceId}}", "functions", "state"] } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State unchanged after invalid exit', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('data-initialized'); });"] } }] + }, + { + "name": "Adim 4: Exit gecerli payload", + "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/transitions/exit-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{exitInstanceId}}", "transitions", "exit-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"exitReason\": \"Test completed successfully\",\n \"exitCode\": \"success\"\n}", "options": { "raw": { "language": "json" } } } }, + "response": [] + }, + { + "name": "Adim 5: Durum Kontrol (exited-state, status C)", + "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{exitInstanceId}}", "functions", "state"] } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State is exited-state', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('exited-state'); });", "pm.test('Status is C', function () { pm.expect(jsonData.status || (jsonData.data && jsonData.data.status)).to.equal('C'); });"] } }] + } + ] + }, + { + "name": "TEST 9: SubFlow + UpdateData Schema", + "item": [ + { + "name": "Adim 1: Instance Baslat", + "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-subflow-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-validation\", \"subflow-updatedata\"],\n \"attributes\": {\n \"orderId\": \"ORD-SUBFLOW-001\",\n \"customerName\": \"SubFlow Test\",\n \"amount\": 2000,\n \"currency\": \"TRY\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "response": [], + "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "if (jsonData && jsonData.id) { pm.collectionVariables.set('subflowInstanceId', jsonData.id); }"] } }] + }, + { + "name": "Adim 2: confirm-with-schema", + "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/confirm-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{subflowInstanceId}}", "transitions", "confirm-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"confirmed\": true,\n \"confirmedBy\": \"test-admin\"\n}", "options": { "raw": { "language": "json" } } } }, + "response": [] + }, + { + "name": "Adim 3: enter-subflow", + "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/enter-subflow?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{subflowInstanceId}}", "transitions", "enter-subflow"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } }, + "response": [] + }, + { + "name": "Adim 4: UpdateData eksik notes — reddedilmeli", + "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/update-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{subflowInstanceId}}", "transitions", "update-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"someOtherField\": \"not notes\"\n}", "options": { "raw": { "language": "json" } } } }, + "response": [] + }, + { + "name": "Adim 5: UpdateData gecerli payload", + "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/update-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{subflowInstanceId}}", "transitions", "update-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"notes\": \"Valid update data notes\"\n}", "options": { "raw": { "language": "json" } } } }, + "response": [] + } + ] + } + ] +} diff --git a/api-tests/schema-validation/schema-validation-test-workflow.http b/api-tests/schema-validation/schema-validation-test-workflow.http new file mode 100644 index 0000000..2fbbac8 --- /dev/null +++ b/api-tests/schema-validation/schema-validation-test-workflow.http @@ -0,0 +1,437 @@ +@baseUrl = http://localhost:4201 +@apiVersion = 1 +@domain = core +@workflow = schema-validation-test-workflow + +############################################################################### +# TEST 1: Happy Path — Master Schema + Transition Schema + Completion +# Tests: master schema active, transition schema enforcement, +# onExecutionTasks with mapping, full chain to status C +############################################################################### + +### Adim 1: Workflow Baslat (master schema required alanlarla) +# @name createSchemaInstance +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "schema-validation-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "schema"], + "attributes": { + "orderId": "ORD-001", + "customerName": "Test Customer", + "amount": 1500.50, + "currency": "TRY" + } +} + +@schemaInstanceId = {{createSchemaInstance.response.body.$.id}} + +### Adim 2: Durum Kontrol (data-initialized olmali) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state + +### Adim 3: Instance Data Kontrol (SchemaInitMapping sonrasi) +# status="initialized", internalNote, auditLog alanlari olmali +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/data + +### Adim 4: Transition Schema ile Onayla (dogru data) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/confirm-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "confirmed": true, + "confirmedBy": "test-admin" + } +} + +### Adim 5: Durum Kontrol (schema-validated-state olmali) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state + +### Adim 6: No-schema state'e gec +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/to-no-schema?sync=true +Content-Type: application/json + +{} + +### Adim 7: Tamamla +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/complete-schema-validation-test?sync=true +Content-Type: application/json + +{} + +### Adim 8: Final Durum Kontrol (completed-state, status: C olmali) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state + +############################################################################### +# TEST 2: Transition Schema Rejection (Sessiz Red) +# Tests: yanlis data gonderilirse transition 200 doner ama state DEGISMEZ +############################################################################### + +### Adim 1: Yeni Instance Baslat +# @name createRejectInstance +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "schema-reject-test-{{$timestamp}}", + "tags": ["integration-test", "schema", "rejection"], + "attributes": { + "orderId": "ORD-002", + "customerName": "Reject Test", + "amount": 500, + "currency": "USD" + } +} + +@rejectInstanceId = {{createRejectInstance.response.body.$.id}} + +### Adim 2: Transition Schema ile YANLIS data gonder (confirmed ve confirmedBy required) +# Bu 200 donecek ama state DEGISMEMELI (sessiz red) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/transitions/confirm-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "wrongField": "this does not match schema" + } +} + +### Adim 3: Durum Kontrol (HALA data-initialized olmali - state degismemeli) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/functions/state + +### Adim 4: Eksik required alan ile dene (confirmedBy yok) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/transitions/confirm-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "confirmed": true + } +} + +### Adim 5: Durum Kontrol (HALA data-initialized olmali) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/functions/state + +############################################################################### +# TEST 3: Master Schema Violation at Start +# Tests: master schema zorunlu alan eksik olunca baslatma basarisiz olmali +############################################################################### + +### Adim 1: Eksik required alan ile baslat (currency ve amount yok) +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "schema-violation-test-{{$timestamp}}", + "tags": ["integration-test", "schema", "violation"], + "attributes": { + "orderId": "ORD-004", + "customerName": "Missing Currency" + } +} + +############################################################################### +# TEST 4: Field Roles — Master Schema alan bazli gorunurluk +# Tests: role header'a gore internalNote ve auditLog filtrelenir +############################################################################### + +### Adim 1: Instance baslat (field roles testi icin) +# @name createFieldRolesInstance +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "schema-fieldroles-test-{{$timestamp}}", + "tags": ["integration-test", "schema", "field-roles"], + "attributes": { + "orderId": "ORD-005", + "customerName": "Field Roles Test", + "amount": 1000, + "currency": "TRY" + } +} + +@fieldRolesInstanceId = {{createFieldRolesInstance.response.body.$.id}} + +### Adim 2: admin rolu ile data oku — internalNote GORUNMELI +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data +role: admin + +### Adim 3: customer rolu ile data oku — internalNote GORUNMEMELI +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data +role: customer + +### Adim 4: auditor rolu ile data oku — auditLog GORUNMELI +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data +role: auditor + +### Adim 5: rol belirtmeden data oku — internalNote ve auditLog GORUNMEMELI +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data + +############################################################################### +# TEST 5: Schema-siz Transition (Serbest gecis) +# Tests: schema olmayan transition herhangi body kabul eder +############################################################################### + +### Adim 1: Instance baslat +# @name createNoSchemaInstance +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "schema-validation-noschema-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "schema", "no-schema"], + "attributes": { + "orderId": "ORD-007", + "customerName": "No Schema Test", + "amount": 300, + "currency": "EUR" + } +} + +@noSchemaInstanceId = {{createNoSchemaInstance.response.body.$.id}} + +### Adim 2: skip-to-no-schema (schema yok, bos body ile gecis yapilabilmeli) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/transitions/skip-to-no-schema?sync=true +Content-Type: application/json + +{} + +### Adim 3: Durum Kontrol (no-schema-state olmali) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/functions/state + +### Adim 4: complete-schema-validation-test ile tamamla +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/transitions/complete-schema-validation-test?sync=true +Content-Type: application/json + +{} + +### Adim 5: Final Durum (completed-state, status: C) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/functions/state + +############################################################################### +# TEST 6: Start Transition Schema Rejection +# Tests: startTransition.schema zorunlu alan eksik olunca baslatma reddedilmeli +############################################################################### + +### Adim 1: Eksik required alan ile baslat (orderId, amount, currency yok) +# startTransition schema: required = orderId, customerName, amount, currency +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "schema-start-reject-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "start-schema-reject"], + "attributes": { + "customerName": "Only name provided" + } +} + +############################################################################### +# TEST 7: Manual Transition Schema Silent Rejection (confirm-with-schema) +# Tests: schema required alanlar eksik olunca transition 200 doner ama state degismez +############################################################################### + +### Adim 1: Yeni Instance Baslat +# @name createManualSchemaRejectInstance +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "schema-manual-reject-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "manual-schema-reject"], + "attributes": { + "orderId": "ORD-MANUAL-001", + "customerName": "Manual Schema Test", + "amount": 250, + "currency": "EUR" + } +} + +@manualRejectInstanceId = {{createManualSchemaRejectInstance.response.body.$.id}} + +### Adim 2: Type mismatch ile dene (confirmed: "yes" string, confirmedBy: 123 number) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{manualRejectInstanceId}}/transitions/confirm-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "confirmed": "yes", + "confirmedBy": 123 + } +} + +### Adim 3: Durum Kontrol (HALA data-initialized olmali) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{manualRejectInstanceId}}/functions/state + +############################################################################### +# TEST 8: Cancel Transition Schema (happy + invalid) +# Tests: cancel schema ile gecerli/gecersiz payload testi +############################################################################### + +### Adim 1: Instance Baslat +# @name createCancelInstance +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "schema-cancel-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "cancel-schema"], + "attributes": { + "orderId": "ORD-CANCEL-001", + "customerName": "Cancel Test", + "amount": 500, + "currency": "TRY" + } +} + +@cancelInstanceId = {{createCancelInstance.response.body.$.id}} + +### Adim 2: Cancel ile EKSIK required alan (cancelReason yok) — reddedilmeli +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/transitions/cancel-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "cancelledBy": "test-user" + } +} + +### Adim 3: Durum Kontrol (HALA data-initialized olmali) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/functions/state + +### Adim 4: Cancel ile GECERLI payload +# @name cancelWithValidPayload +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/transitions/cancel-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "cancelReason": "No longer needed for testing", + "cancelledBy": "test-admin" + } +} + +### Adim 5: Durum Kontrol (cancelled-state, status: C olmali) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/functions/state + +############################################################################### +# TEST 9: Exit Transition Schema (happy + invalid) +# Tests: exit schema ile gecerli/gecersiz payload testi +############################################################################### + +### Adim 1: Instance Baslat +# @name createExitInstance +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "schema-exit-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "exit-schema"], + "attributes": { + "orderId": "ORD-EXIT-001", + "customerName": "Exit Test", + "amount": 750, + "currency": "USD" + } +} + +@exitInstanceId = {{createExitInstance.response.body.$.id}} + +### Adim 2: Exit ile GECERSIZ exitCode enum (invalid-code) — reddedilmeli +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/transitions/exit-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "exitReason": "Testing invalid code", + "exitCode": "invalid-code" + } +} + +### Adim 3: Durum Kontrol (HALA data-initialized olmali) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/functions/state + +### Adim 4: Exit ile GECERLI payload +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/transitions/exit-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "exitReason": "Test completed successfully", + "exitCode": "success" + } +} + +### Adim 5: Durum Kontrol (exited-state, status: C olmali) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/functions/state + +############################################################################### +# TEST 10: SubFlow + UpdateData Schema (happy + invalid) +# Tests: SubFlow state'te updateData schema validasyonu +############################################################################### + +### Adim 1: Instance Baslat +# @name createSubflowInstance +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "schema-subflow-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "subflow-updatedata"], + "attributes": { + "orderId": "ORD-SUBFLOW-001", + "customerName": "SubFlow Test", + "amount": 2000, + "currency": "TRY" + } +} + +@subflowInstanceId = {{createSubflowInstance.response.body.$.id}} + +### Adim 2: confirm-with-schema ile gecis +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/confirm-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "confirmed": true, + "confirmedBy": "test-admin" + } +} + +### Adim 3: SubFlow state'e gec +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/enter-subflow?sync=true +Content-Type: application/json + +{} + +### Adim 4: Durum Kontrol (subflow-state olmali) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/functions/state + +### Adim 5: UpdateData ile EKSIK required alan (notes yok) — reddedilmeli +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/update-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "someOtherField": "not notes" + } +} + +### Adim 6: Durum Kontrol (hala subflow-state olmali, eTag degismemeli) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/functions/state + +### Adim 7: UpdateData ile GECERLI payload (notes min 3 karakter) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/update-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "notes": "Valid update data notes" + } +} + +### Adim 8: Instance Body Kontrol (eTag degismis olmali) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}} diff --git a/core.link.json b/core.link.json index 46acb9a..49cf9ce 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-05-06T06:49:44.323Z" + "importedAt": "2026-05-07T11:59:04.804Z" } \ No newline at end of file diff --git a/core/Schemas/schema-validation/schema-validation-cancel.json b/core/Schemas/schema-validation/schema-validation-cancel.json new file mode 100644 index 0000000..589ead1 --- /dev/null +++ b/core/Schemas/schema-validation/schema-validation-cancel.json @@ -0,0 +1,43 @@ +{ + "key": "schema-validation-cancel", + "version": "1.0.0", + "domain": "core", + "flow": "sys-schemas", + "flowVersion": "1.0.0", + "tags": [ + "integration-test", + "schema-validation", + "schema", + "cancel-schema" + ], + "attributes": { + "type": "workflow", + "labels": [ + { + "language": "en-US", + "label": "Schema Validation Cancel Transition Schema" + } + ], + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://core.burgan.com/schemas/schema-validation-cancel", + "title": "Cancel Transition Payload Schema", + "type": "object", + "required": [ + "cancelReason", + "cancelledBy" + ], + "properties": { + "cancelReason": { + "type": "string", + "minLength": 5 + }, + "cancelledBy": { + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": true + } + } +} diff --git a/core/Schemas/schema-data/schema-data-confirm-transition.json b/core/Schemas/schema-validation/schema-validation-confirm-transition.json similarity index 77% rename from core/Schemas/schema-data/schema-data-confirm-transition.json rename to core/Schemas/schema-validation/schema-validation-confirm-transition.json index 82985b5..0171974 100644 --- a/core/Schemas/schema-data/schema-data-confirm-transition.json +++ b/core/Schemas/schema-validation/schema-validation-confirm-transition.json @@ -1,27 +1,24 @@ { - "key": "schema-data-confirm-transition", + "key": "schema-validation-confirm-transition", "version": "1.0.0", "domain": "core", "flow": "sys-schemas", - "flowVersion": "1.0.0", "tags": [ "integration-test", - "schema-data", + "schema-validation", + "schema", "transition-schema" ], "attributes": { - "type": "workflow", - "labels": [ - { - "language": "en-US", - "label": "Confirm Transition Schema" - } - ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://core.burgan.com/schemas/schema-data-confirm-transition", + "$id": "https://core.burgan.com/schemas/schema-validation-confirm-transition", "title": "Confirm Transition Payload Schema", "type": "object", + "required": [ + "confirmed", + "confirmedBy" + ], "properties": { "confirmed": { "type": "boolean" @@ -31,11 +28,15 @@ "minLength": 1 } }, - "required": [ - "confirmed", - "confirmedBy" - ], "additionalProperties": true - } - } + }, + "type": "workflow", + "labels": [ + { + "language": "en-US", + "label": "Confirm Transition Schema" + } + ] + }, + "flowVersion": "1.0.0" } diff --git a/core/Schemas/schema-validation/schema-validation-exit.json b/core/Schemas/schema-validation/schema-validation-exit.json new file mode 100644 index 0000000..c909de5 --- /dev/null +++ b/core/Schemas/schema-validation/schema-validation-exit.json @@ -0,0 +1,43 @@ +{ + "key": "schema-validation-exit", + "version": "1.0.0", + "domain": "core", + "flow": "sys-schemas", + "flowVersion": "1.0.0", + "tags": [ + "integration-test", + "schema-validation", + "schema", + "exit-schema" + ], + "attributes": { + "type": "workflow", + "labels": [ + { + "language": "en-US", + "label": "Schema Validation Exit Transition Schema" + } + ], + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://core.burgan.com/schemas/schema-validation-exit", + "title": "Exit Transition Payload Schema", + "type": "object", + "required": [ + "exitReason", + "exitCode" + ], + "properties": { + "exitReason": { + "type": "string", + "minLength": 1 + }, + "exitCode": { + "type": "string", + "enum": ["success", "failure", "timeout"] + } + }, + "additionalProperties": true + } + } +} diff --git a/core/Schemas/schema-data/schema-data-master.json b/core/Schemas/schema-validation/schema-validation-master.json similarity index 80% rename from core/Schemas/schema-data/schema-data-master.json rename to core/Schemas/schema-validation/schema-validation-master.json index cd68273..a10927a 100644 --- a/core/Schemas/schema-data/schema-data-master.json +++ b/core/Schemas/schema-validation/schema-validation-master.json @@ -1,12 +1,13 @@ { - "key": "schema-data-master", + "key": "schema-validation-master", "version": "1.0.0", "domain": "core", "flow": "sys-schemas", "flowVersion": "1.0.0", "tags": [ "integration-test", - "schema-data", + "schema-validation", + "schema", "master-schema", "field-roles" ], @@ -15,13 +16,13 @@ "labels": [ { "language": "en-US", - "label": "Schema Data Test Master Schema" + "label": "Schema Validation Test Master Schema" } ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://core.burgan.com/schemas/schema-data-master", - "title": "Schema Data Test Master Schema", + "$id": "https://core.burgan.com/schemas/schema-validation-master", + "title": "Schema Validation Test Master Schema", "type": "object", "properties": { "orderId": { @@ -69,6 +70,12 @@ } ] }, + "confirmed": { + "type": "boolean" + }, + "confirmedBy": { + "type": "string" + }, "updatedAt": { "type": "string", "format": "date-time" diff --git a/core/Schemas/schema-validation/schema-validation-start.json b/core/Schemas/schema-validation/schema-validation-start.json new file mode 100644 index 0000000..13df500 --- /dev/null +++ b/core/Schemas/schema-validation/schema-validation-start.json @@ -0,0 +1,53 @@ +{ + "key": "schema-validation-start", + "version": "1.0.0", + "domain": "core", + "flow": "sys-schemas", + "flowVersion": "1.0.0", + "tags": [ + "integration-test", + "schema-validation", + "schema", + "start-schema" + ], + "attributes": { + "type": "workflow", + "labels": [ + { + "language": "en-US", + "label": "Schema Validation Start Transition Schema" + } + ], + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://core.burgan.com/schemas/schema-validation-start", + "title": "Start Transition Payload Schema", + "type": "object", + "required": [ + "orderId", + "customerName", + "amount", + "currency" + ], + "properties": { + "orderId": { + "type": "string", + "minLength": 1 + }, + "customerName": { + "type": "string", + "minLength": 1 + }, + "amount": { + "type": "number", + "minimum": 0 + }, + "currency": { + "type": "string", + "enum": ["TRY", "USD", "EUR"] + } + }, + "additionalProperties": true + } + } +} diff --git a/core/Schemas/schema-validation/schema-validation-update-data.json b/core/Schemas/schema-validation/schema-validation-update-data.json new file mode 100644 index 0000000..526b71e --- /dev/null +++ b/core/Schemas/schema-validation/schema-validation-update-data.json @@ -0,0 +1,38 @@ +{ + "key": "schema-validation-update-data", + "version": "1.0.0", + "domain": "core", + "flow": "sys-schemas", + "flowVersion": "1.0.0", + "tags": [ + "integration-test", + "schema-validation", + "schema", + "update-data-schema" + ], + "attributes": { + "type": "workflow", + "labels": [ + { + "language": "en-US", + "label": "Schema Validation UpdateData Transition Schema" + } + ], + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://core.burgan.com/schemas/schema-validation-update-data", + "title": "UpdateData Transition Payload Schema", + "type": "object", + "required": [ + "notes" + ], + "properties": { + "notes": { + "type": "string", + "minLength": 3 + } + }, + "additionalProperties": true + } + } +} diff --git a/core/Tasks/schema-data/schema-data-script-task.json b/core/Tasks/schema-validation/schema-validation-script-task.json similarity index 56% rename from core/Tasks/schema-data/schema-data-script-task.json rename to core/Tasks/schema-validation/schema-validation-script-task.json index e2d1ff7..6d14142 100644 --- a/core/Tasks/schema-data/schema-data-script-task.json +++ b/core/Tasks/schema-validation/schema-validation-script-task.json @@ -1,10 +1,10 @@ { - "key": "schema-data-script-task", + "key": "schema-validation-script-task", "version": "1.0.0", "domain": "core", "flow": "sys-tasks", "flowVersion": "1.0.0", - "tags": ["integration-test", "schema-data", "script"], + "tags": ["integration-test", "schema-validation", "schema", "script"], "attributes": { "type": "7", "config": {} diff --git a/core/Workflows/schema-data/.meta/schema-data-test-workflow.diagram.json b/core/Workflows/schema-data/.meta/schema-data-test-workflow.diagram.json deleted file mode 100644 index 06f5dd6..0000000 --- a/core/Workflows/schema-data/.meta/schema-data-test-workflow.diagram.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "nodePos": { - "data-initialized": { - "x": 60, - "y": 560 - }, - "schema-validated-state": { - "x": 560, - "y": -260 - }, - "no-schema-state": { - "x": 880, - "y": 580 - }, - "completed-state": { - "x": 1380, - "y": -20 - }, - "__start__": { - "x": -296, - "y": 140 - } - } -} \ No newline at end of file diff --git a/core/Workflows/schema-data/schema-data-test-workflow.json b/core/Workflows/schema-data/schema-data-test-workflow.json deleted file mode 100644 index 7297e47..0000000 --- a/core/Workflows/schema-data/schema-data-test-workflow.json +++ /dev/null @@ -1,230 +0,0 @@ -{ - "key": "schema-data-test-workflow", - "flow": "sys-flows", - "flowVersion": "1.0.0", - "domain": "core", - "version": "1.0.0", - "tags": [ - "integration-test", - "schema-data", - "master-schema", - "transition-schema", - "schema-rejection", - "update-data", - "field-roles", - "etag" - ], - "attributes": { - "type": "F", - "timeout": null, - "labels": [ - { - "language": "en-US", - "label": "Schema & Data Management Integration Test" - } - ], - "functions": [], - "features": [], - "extensions": [], - "sharedTransitions": [], - "schema": { - "schema": { - "key": "schema-data-master", - "domain": "core", - "version": "1.0.0", - "flow": "sys-schemas" - } - }, - "updateData": { - "key": "update-instance-data", - "target": "$self", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Update Instance Data (updateData $self)" - } - ] - }, - "startTransition": { - "key": "start-schema-test", - "target": "data-initialized", - "triggerType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Start Schema Test" - } - ], - "onExecutionTasks": [ - { - "order": 1, - "task": { - "key": "schema-data-script-task", - "domain": "core", - "version": "1.0.0", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/SchemaInitMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFNjaGVtYUluaXRNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKCiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgIm9yZGVySWQiKSkKICAgICAgICAgICAgcmVzdWx0Lm9yZGVySWQgPSBkYXRhLm9yZGVySWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjdXN0b21lck5hbWUiKSkKICAgICAgICAgICAgcmVzdWx0LmN1c3RvbWVyTmFtZSA9IGRhdGEuY3VzdG9tZXJOYW1lOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiYW1vdW50IikpCiAgICAgICAgICAgIHJlc3VsdC5hbW91bnQgPSBkYXRhLmFtb3VudDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImN1cnJlbmN5IikpCiAgICAgICAgICAgIHJlc3VsdC5jdXJyZW5jeSA9IGRhdGEuY3VycmVuY3k7CgogICAgICAgIHJlc3VsdC5zdGF0dXMgPSAiaW5pdGlhbGl6ZWQiOwogICAgICAgIHJlc3VsdC5pbnRlcm5hbE5vdGUgPSAiQ3JlYXRlZCBieSBpbnRlZ3JhdGlvbiB0ZXN0IjsKICAgICAgICByZXN1bHQuYXVkaXRMb2cgPSAiSW5pdCBhdCAiICsgRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCQiU2NoZW1hSW5pdE1hcHBpbmc6IG9yZGVyPXtyZXN1bHQub3JkZXJJZH0iKTsKCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" - } - } - ] - }, - "states": [ - { - "key": "data-initialized", - "stateType": 1, - "subType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Data Initialized (Master Schema Active)" - } - ], - "view": null, - "subFlow": null, - "onEntries": [], - "onExits": [], - "transitions": [ - { - "key": "confirm-with-schema", - "target": "schema-validated-state", - "triggerType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Confirm with Transition Schema" - } - ], - "schema": { - "key": "schema-data-confirm-transition", - "domain": "core", - "version": "1.0.0", - "flow": "sys-schemas" - }, - "view": null, - "onExecutionTasks": [ - { - "order": 1, - "task": { - "key": "schema-data-script-task", - "domain": "core", - "version": "1.0.0", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/ConfirmMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIENvbmZpcm1NYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKCiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgIm9yZGVySWQiKSkKICAgICAgICAgICAgcmVzdWx0Lm9yZGVySWQgPSBkYXRhLm9yZGVySWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjdXN0b21lck5hbWUiKSkKICAgICAgICAgICAgcmVzdWx0LmN1c3RvbWVyTmFtZSA9IGRhdGEuY3VzdG9tZXJOYW1lOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiYW1vdW50IikpCiAgICAgICAgICAgIHJlc3VsdC5hbW91bnQgPSBkYXRhLmFtb3VudDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImN1cnJlbmN5IikpCiAgICAgICAgICAgIHJlc3VsdC5jdXJyZW5jeSA9IGRhdGEuY3VycmVuY3k7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpbnRlcm5hbE5vdGUiKSkKICAgICAgICAgICAgcmVzdWx0LmludGVybmFsTm90ZSA9IGRhdGEuaW50ZXJuYWxOb3RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiYXVkaXRMb2ciKSkKICAgICAgICAgICAgcmVzdWx0LmF1ZGl0TG9nID0gZGF0YS5hdWRpdExvZzsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNvbmZpcm1lZCIpKQogICAgICAgICAgICByZXN1bHQuY29uZmlybWVkID0gZGF0YS5jb25maXJtZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjb25maXJtZWRCeSIpKQogICAgICAgICAgICByZXN1bHQuY29uZmlybWVkQnkgPSBkYXRhLmNvbmZpcm1lZEJ5OwoKICAgICAgICByZXN1bHQuc3RhdHVzID0gImNvbmZpcm1lZCI7CiAgICAgICAgcmVzdWx0LnVwZGF0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkNvbmZpcm1NYXBwaW5nOiBjb25maXJtZWQgYnkge3Jlc3VsdC5jb25maXJtZWRCeX0iKTsKCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" - } - } - ] - }, - { - "key": "skip-to-no-schema", - "target": "no-schema-state", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Skip to No-Schema State" - } - ], - "schema": null, - "view": null, - "onExecutionTasks": [] - } - ] - }, - { - "key": "schema-validated-state", - "stateType": 2, - "subType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Schema Validated State" - } - ], - "view": null, - "subFlow": null, - "onEntries": [], - "onExits": [], - "transitions": [ - { - "key": "to-no-schema", - "target": "no-schema-state", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Move to No-Schema State" - } - ], - "schema": null, - "view": null, - "onExecutionTasks": [] - } - ] - }, - { - "key": "no-schema-state", - "stateType": 2, - "subType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "No Schema State (Free-form transitions)" - } - ], - "view": null, - "subFlow": null, - "onEntries": [], - "onExits": [], - "transitions": [ - { - "key": "complete-schema-test", - "target": "completed-state", - "triggerType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Complete Schema Test" - } - ], - "schema": null, - "view": null, - "onExecutionTasks": [] - } - ] - }, - { - "key": "completed-state", - "stateType": 3, - "subType": 1, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Schema Test Completed (Final/Success)" - } - ], - "view": null, - "subFlow": null, - "onEntries": [], - "onExits": [], - "transitions": [] - } - ] - } -} \ No newline at end of file diff --git a/core/Workflows/schema-validation/.meta/schema-validation-subflow-child.diagram.json b/core/Workflows/schema-validation/.meta/schema-validation-subflow-child.diagram.json new file mode 100644 index 0000000..00a4467 --- /dev/null +++ b/core/Workflows/schema-validation/.meta/schema-validation-subflow-child.diagram.json @@ -0,0 +1,3 @@ +{ + "nodePos": {} +} \ No newline at end of file diff --git a/core/Workflows/schema-validation/.meta/schema-validation-test-workflow.diagram.json b/core/Workflows/schema-validation/.meta/schema-validation-test-workflow.diagram.json new file mode 100644 index 0000000..00a4467 --- /dev/null +++ b/core/Workflows/schema-validation/.meta/schema-validation-test-workflow.diagram.json @@ -0,0 +1,3 @@ +{ + "nodePos": {} +} \ No newline at end of file diff --git a/core/Workflows/schema-validation/schema-validation-subflow-child.json b/core/Workflows/schema-validation/schema-validation-subflow-child.json new file mode 100644 index 0000000..91953e4 --- /dev/null +++ b/core/Workflows/schema-validation/schema-validation-subflow-child.json @@ -0,0 +1,107 @@ +{ + "key": "schema-validation-subflow-child", + "flow": "sys-flows", + "flowVersion": "1.0.0", + "domain": "core", + "version": "1.0.0", + "tags": ["integration-test", "schema-validation", "subflow", "child"], + "attributes": { + "type": "S", + "timeout": null, + "labels": [ + { + "language": "en-US", + "label": "Schema Validation SubFlow Child" + } + ], + "functions": [], + "features": [], + "extensions": [], + "sharedTransitions": [], + "updateData": { + "key": "update-with-schema", + "target": "$self", + "triggerType": 0, + "versionStrategy": "Minor", + "schema": { + "key": "schema-validation-update-data", + "domain": "core", + "version": "1.0.0", + "flow": "sys-schemas" + }, + "labels": [ + { + "language": "en-US", + "label": "Update Parent Data with Schema" + } + ], + "onExecutionTasks": [] + }, + "startTransition": { + "key": "start-subflow-child", + "target": "subflow-child-active", + "triggerType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Start SubFlow Child" + } + ], + "onExecutionTasks": [] + }, + "states": [ + { + "key": "subflow-child-active", + "stateType": 1, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "SubFlow Child Active" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "complete-subflow-child", + "target": "subflow-child-completed", + "triggerType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Complete SubFlow Child" + } + ], + "schema": null, + "view": null, + "mapping": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "subflow-child-completed", + "stateType": 3, + "subType": 1, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "SubFlow Child Completed" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [] + } + ] + } +} diff --git a/core/Workflows/schema-validation/schema-validation-test-workflow.json b/core/Workflows/schema-validation/schema-validation-test-workflow.json new file mode 100644 index 0000000..5b8d0ce --- /dev/null +++ b/core/Workflows/schema-validation/schema-validation-test-workflow.json @@ -0,0 +1,370 @@ +{ + "key": "schema-validation-test-workflow", + "flow": "sys-flows", + "flowVersion": "1.0.0", + "domain": "core", + "version": "1.0.0", + "tags": [ + "integration-test", + "schema-validation", + "schema", + "master-schema", + "transition-schema", + "schema-rejection", + "field-roles", + "cancel", + "exit", + "update-data", + "subflow" + ], + "attributes": { + "type": "F", + "timeout": null, + "labels": [ + { + "language": "en-US", + "label": "Schema Validation Integration Test" + } + ], + "functions": [], + "features": [], + "extensions": [], + "sharedTransitions": [], + "schema": { + "key": "schema-validation-master", + "domain": "core", + "version": "1.0.0", + "flow": "sys-schemas" + }, + "cancel": { + "key": "cancel-with-schema", + "target": "cancelled-state", + "triggerType": 0, + "versionStrategy": "Major", + "availableIn": [], + "schema": { + "key": "schema-validation-cancel", + "domain": "core", + "version": "1.0.0", + "flow": "sys-schemas" + }, + "labels": [ + { + "language": "en-US", + "label": "Cancel with Schema Validation" + } + ], + "onExecutionTasks": [] + }, + "exit": { + "key": "exit-with-schema", + "target": "exited-state", + "triggerType": 0, + "versionStrategy": "Major", + "availableIn": [ + "data-initialized", + "schema-validated-state", + "no-schema-state", + "subflow-state" + ], + "schema": { + "key": "schema-validation-exit", + "domain": "core", + "version": "1.0.0", + "flow": "sys-schemas" + }, + "labels": [ + { + "language": "en-US", + "label": "Exit with Schema Validation" + } + ], + "onExecutionTasks": [] + }, + "startTransition": { + "key": "start-schema-validation-test", + "target": "data-initialized", + "triggerType": 0, + "versionStrategy": "Major", + "schema": { + "key": "schema-validation-start", + "domain": "core", + "version": "1.0.0", + "flow": "sys-schemas" + }, + "labels": [ + { + "language": "en-US", + "label": "Start Schema Validation Test" + } + ], + "onExecutionTasks": [ + { + "order": 1, + "task": { + "key": "schema-validation-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/SchemaInitMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFNjaGVtYUluaXRNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKCiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgIm9yZGVySWQiKSkKICAgICAgICAgICAgcmVzdWx0Lm9yZGVySWQgPSBkYXRhLm9yZGVySWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjdXN0b21lck5hbWUiKSkKICAgICAgICAgICAgcmVzdWx0LmN1c3RvbWVyTmFtZSA9IGRhdGEuY3VzdG9tZXJOYW1lOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiYW1vdW50IikpCiAgICAgICAgICAgIHJlc3VsdC5hbW91bnQgPSBkYXRhLmFtb3VudDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImN1cnJlbmN5IikpCiAgICAgICAgICAgIHJlc3VsdC5jdXJyZW5jeSA9IGRhdGEuY3VycmVuY3k7CgogICAgICAgIHJlc3VsdC5zdGF0dXMgPSAiaW5pdGlhbGl6ZWQiOwogICAgICAgIHJlc3VsdC5pbnRlcm5hbE5vdGUgPSAiQ3JlYXRlZCBieSBpbnRlZ3JhdGlvbiB0ZXN0IjsKICAgICAgICByZXN1bHQuYXVkaXRMb2cgPSAiSW5pdCBhdCAiICsgRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCQiU2NoZW1hSW5pdE1hcHBpbmc6IG9yZGVyPXtyZXN1bHQub3JkZXJJZH0iKTsKCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" + } + } + ] + }, + "states": [ + { + "key": "data-initialized", + "stateType": 1, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Data Initialized (Master Schema Active)" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "confirm-with-schema", + "target": "schema-validated-state", + "triggerType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Confirm with Transition Schema" + } + ], + "schema": { + "key": "schema-validation-confirm-transition", + "domain": "core", + "version": "1.0.0", + "flow": "sys-schemas" + }, + "view": null, + "mapping": null, + "onExecutionTasks": [] + }, + { + "key": "skip-to-no-schema", + "target": "no-schema-state", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Skip to No-Schema State" + } + ], + "schema": null, + "view": null, + "mapping": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "schema-validated-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Schema Validated State" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "schema-validation-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/ConfirmExecutionMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIENvbmZpcm1FeGVjdXRpb25NYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKCiAgICAgICAgdmFyIGhhc0NvbmZpcm1lZCA9IEhhc1Byb3BlcnR5KGRhdGEsICJjb25maXJtZWQiKTsKICAgICAgICB2YXIgaGFzQ29uZmlybWVkQnkgPSBIYXNQcm9wZXJ0eShkYXRhLCAiY29uZmlybWVkQnkiKTsKICAgICAgICB2YXIgaGFzT3JkZXJJZCA9IEhhc1Byb3BlcnR5KGRhdGEsICJvcmRlcklkIik7CiAgICAgICAgdmFyIGhhc0ludGVybmFsTm90ZSA9IEhhc1Byb3BlcnR5KGRhdGEsICJpbnRlcm5hbE5vdGUiKTsKICAgICAgICBMb2dJbmZvcm1hdGlvbigkIkNvbmZpcm1FeGVjdXRpb25NYXBwaW5nOiBoYXNDb25maXJtZWQ9e2hhc0NvbmZpcm1lZH0sIGhhc0NvbmZpcm1lZEJ5PXtoYXNDb25maXJtZWRCeX0sIGhhc09yZGVySWQ9e2hhc09yZGVySWR9LCBoYXNJbnRlcm5hbE5vdGU9e2hhc0ludGVybmFsTm90ZX0iKTsKCiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgIm9yZGVySWQiKSkKICAgICAgICAgICAgcmVzdWx0Lm9yZGVySWQgPSBkYXRhLm9yZGVySWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjdXN0b21lck5hbWUiKSkKICAgICAgICAgICAgcmVzdWx0LmN1c3RvbWVyTmFtZSA9IGRhdGEuY3VzdG9tZXJOYW1lOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiYW1vdW50IikpCiAgICAgICAgICAgIHJlc3VsdC5hbW91bnQgPSBkYXRhLmFtb3VudDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImN1cnJlbmN5IikpCiAgICAgICAgICAgIHJlc3VsdC5jdXJyZW5jeSA9IGRhdGEuY3VycmVuY3k7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJpbnRlcm5hbE5vdGUiKSkKICAgICAgICAgICAgcmVzdWx0LmludGVybmFsTm90ZSA9IGRhdGEuaW50ZXJuYWxOb3RlOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiYXVkaXRMb2ciKSkKICAgICAgICAgICAgcmVzdWx0LmF1ZGl0TG9nID0gZGF0YS5hdWRpdExvZzsKCiAgICAgICAgcmVzdWx0LmNvbmZpcm1lZCA9IEhhc1Byb3BlcnR5KGRhdGEsICJjb25maXJtZWQiKSA/IGRhdGEuY29uZmlybWVkIDogdHJ1ZTsKICAgICAgICByZXN1bHQuY29uZmlybWVkQnkgPSBIYXNQcm9wZXJ0eShkYXRhLCAiY29uZmlybWVkQnkiKQogICAgICAgICAgICA/IGRhdGEuY29uZmlybWVkQnkKICAgICAgICAgICAgOiAic3lzdGVtLWNvbmZpcm1lZCI7CgogICAgICAgIHJlc3VsdC5zdGF0dXMgPSAiY29uZmlybWVkIjsKICAgICAgICByZXN1bHQudXBkYXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCQiQ29uZmlybUV4ZWN1dGlvbk1hcHBpbmcgZXhlY3V0ZWQsIHN0YXR1cz1jb25maXJtZWQsIGNvbmZpcm1lZEJ5PXtyZXN1bHQuY29uZmlybWVkQnl9Iik7CgogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "to-no-schema", + "target": "no-schema-state", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Move to No-Schema State" + } + ], + "schema": null, + "view": null, + "mapping": null, + "onExecutionTasks": [] + }, + { + "key": "enter-subflow", + "target": "subflow-state", + "triggerType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Enter SubFlow State" + } + ], + "schema": null, + "view": null, + "mapping": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "no-schema-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "No Schema State (Free-form transitions)" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "complete-schema-validation-test", + "target": "completed-state", + "triggerType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Complete Schema Validation Test" + } + ], + "schema": null, + "view": null, + "mapping": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "subflow-state", + "stateType": 4, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "SubFlow State (updateData test)" + } + ], + "view": null, + "subFlow": { + "type": "S", + "process": { + "key": "schema-validation-subflow-child", + "domain": "core", + "version": "1.0.0", + "flow": "sys-flows" + }, + "mapping": { + "location": "./src/SubflowPassthroughMapping.csx", + "code": "dXNpbmcgU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWM7CnVzaW5nIFN5c3RlbS5EeW5hbWljOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFN1YmZsb3dQYXNzdGhyb3VnaE1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJU3ViRmxvd01hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyBjaGlsZElucHV0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoZGF0YSAhPSBudWxsICYmIEhhc1Byb3BlcnR5KGRhdGEsICJvcmRlcklkIikpCiAgICAgICAgewogICAgICAgICAgICBjaGlsZElucHV0Lm9yZGVySWQgPSBkYXRhLm9yZGVySWQ7CiAgICAgICAgfQogICAgICAgIExvZ0luZm9ybWF0aW9uKCJTdWJmbG93UGFzc3Rocm91Z2hNYXBwaW5nOiBwcmVwYXJlZCBjaGlsZCBpbnB1dCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IGNoaWxkSW5wdXQgfSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQp9Cg==" + } + }, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "subflow-complete", + "target": "completed-state", + "triggerType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Complete from SubFlow" + } + ], + "schema": null, + "view": null, + "mapping": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "completed-state", + "stateType": 3, + "subType": 1, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Schema Validation Test Completed (Final/Success)" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [] + }, + { + "key": "cancelled-state", + "stateType": 3, + "subType": 2, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Cancelled State (Cancel Schema Test)" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [] + }, + { + "key": "exited-state", + "stateType": 3, + "subType": 3, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Exited State (Exit Schema Test)" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [] + } + ] + } +} \ No newline at end of file diff --git a/core/Workflows/schema-data/src/ConfirmMapping.csx b/core/Workflows/schema-validation/src/ConfirmExecutionMapping.csx similarity index 59% rename from core/Workflows/schema-data/src/ConfirmMapping.csx rename to core/Workflows/schema-validation/src/ConfirmExecutionMapping.csx index 01888bc..beb6ef3 100644 --- a/core/Workflows/schema-data/src/ConfirmMapping.csx +++ b/core/Workflows/schema-validation/src/ConfirmExecutionMapping.csx @@ -4,7 +4,7 @@ using System.Threading.Tasks; using BBT.Workflow.Scripting; using BBT.Workflow.Definitions; -public class ConfirmMapping : ScriptBase, IMapping +public class ConfirmExecutionMapping : ScriptBase, IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) { @@ -15,6 +15,12 @@ public class ConfirmMapping : ScriptBase, IMapping { var data = context.Instance.Data; + var hasConfirmed = HasProperty(data, "confirmed"); + var hasConfirmedBy = HasProperty(data, "confirmedBy"); + var hasOrderId = HasProperty(data, "orderId"); + var hasInternalNote = HasProperty(data, "internalNote"); + LogInformation($"ConfirmExecutionMapping: hasConfirmed={hasConfirmed}, hasConfirmedBy={hasConfirmedBy}, hasOrderId={hasOrderId}, hasInternalNote={hasInternalNote}"); + dynamic result = new ExpandoObject(); if (HasProperty(data, "orderId")) @@ -29,15 +35,16 @@ public class ConfirmMapping : ScriptBase, IMapping result.internalNote = data.internalNote; if (HasProperty(data, "auditLog")) result.auditLog = data.auditLog; - if (HasProperty(data, "confirmed")) - result.confirmed = data.confirmed; - if (HasProperty(data, "confirmedBy")) - result.confirmedBy = data.confirmedBy; + + result.confirmed = HasProperty(data, "confirmed") ? data.confirmed : true; + result.confirmedBy = HasProperty(data, "confirmedBy") + ? data.confirmedBy + : "system-confirmed"; result.status = "confirmed"; result.updatedAt = DateTime.UtcNow.ToString("o"); - LogInformation($"ConfirmMapping: confirmed by {result.confirmedBy}"); + LogInformation($"ConfirmExecutionMapping executed, status=confirmed, confirmedBy={result.confirmedBy}"); return Task.FromResult(new ScriptResponse { Data = result }); } diff --git a/core/Workflows/schema-data/src/SchemaInitMapping.csx b/core/Workflows/schema-validation/src/SchemaInitMapping.csx similarity index 100% rename from core/Workflows/schema-data/src/SchemaInitMapping.csx rename to core/Workflows/schema-validation/src/SchemaInitMapping.csx diff --git a/core/Workflows/schema-validation/src/SubflowPassthroughMapping.csx b/core/Workflows/schema-validation/src/SubflowPassthroughMapping.csx new file mode 100644 index 0000000..2e4647d --- /dev/null +++ b/core/Workflows/schema-validation/src/SubflowPassthroughMapping.csx @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; + +public class SubflowPassthroughMapping : ScriptBase, ISubFlowMapping +{ + public Task InputHandler(ScriptContext context) + { + var data = context.Instance.Data; + dynamic childInput = new ExpandoObject(); + if (data != null && HasProperty(data, "orderId")) + { + childInput.orderId = data.orderId; + } + LogInformation("SubflowPassthroughMapping: prepared child input"); + return Task.FromResult(new ScriptResponse { Data = childInput }); + } + + public Task OutputHandler(ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } +} diff --git a/docs/integration-test-documentation.md b/docs/integration-test-documentation.md index 8a10f2a..07ffa6c 100644 --- a/docs/integration-test-documentation.md +++ b/docs/integration-test-documentation.md @@ -13,7 +13,7 @@ Bu dokuman, `core` projesindeki tum integration test workflow'larini, icerdikler | 3 | Task Execution | `task-execution-test-workflow` + `task-target-workflow` + `extended-tasks-test-workflow` | HTTP/Script/StartFlow/GetInstanceData/Notification/TriggerTransition/SubProcess/GetInstances; Dapr HTTP/Service/PubSub (`extended-tasks-test-workflow`); Mocklab + Dapr YAML | | 4 | Error Boundary | `error-boundary-test-workflow` | Retry, Ignore, **Rollback**, **Log**, **Notify** (action:4) aksiyonlari, retry policy, priority, **timeoutPolicy** (onTimeout), **errorHandlerRule** (errorTypes/errorCodes) | | 5 | View, Function & Extension (Comprehensive) | `view-function-extension-test-workflow` | 6 view tipi (JSON/HTML/MD/DeepLink/HTTP/URN), 6 display modu, 4 extension tipi (4 type × 3 scope), 3 function scope (I/F/D), wizard state, implicit global extension, `?extensions=` sorgusu, `functions/view` icerik dogrulamasi | -| 6 | Schema & Data | `schema-data-test-workflow` | Master schema, transition schema, updateData, field roles | +| 6 | Schema Validation | `schema-validation-test-workflow` | Master + start + transition + cancel + exit + updateData schema, field roles, SubFlow updateData | | 7 | Instance Management | `instance-management-test-workflow` | Filtering, pagination, sorting, timeout, idempotent start, **subType 4/5/6** (suspended/busy/human) | | 8 | Flow Types | `core-flow-test` + `subprocess-flow-test` + `flow-flow-test` + `subflow-flow-test` | **Core (C)**, **SubProcess (P)**, **Flow (F)** ve **SubFlow (S)** workflow tipleri | | 9 | Version Consistency | `version-consistency-test-workflow` (v1.0.0 + v2.0.0) | Workflow versiyon degisiminde mevcut instance'larin kendi versiyonlariyla devam etmesi | @@ -772,76 +772,136 @@ view-function-extension-test-workflow (F) --- -## Grup 6: Schema & Data Management +## Grup 6: Schema Validation -**Workflow:** `schema-data-test-workflow` (type: F) +**Workflow:** `schema-validation-test-workflow` (type: F) -**Amac:** Master schema, transition schema validasyonu, updateData ($self) mekanizmasi ve field roles ozelliklerini test eder. +**Amac:** Tum schema baglama noktalarini (master, startTransition, manual transition, cancel, exit, updateData) ve field roles ozelliklerini test eder. **Note (field roles vs. transition roles):** Here **field roles** are **field visibility** on the master schema (e.g. role-based field filtering in `GET .../functions/data`). The transition **`roles`** field on **`lifecycle-transitions`** in **Group 1** is the **`transitions[]` listing filter for `GET .../functions/state`**; the two mechanisms differ. ### Agac Yapisi ``` -schema-data-test-workflow (F) +schema-validation-test-workflow (F) │ -├── [startTransition] start-schema-test +├── [startTransition] start-schema-validation-test +│ ├── schema: schema-validation-start (required: orderId, customerName, amount, currency) │ └── onExecutionTasks: -│ └── schema-data-script-task + SchemaInitMapping.csx +│ └── schema-validation-script-task + SchemaInitMapping.csx │ ├── [workflow-level schema] -│ └── schema-data-master (master schema) +│ └── schema-validation-master (master schema) │ ├── required: orderId, customerName, amount, currency │ ├── enum: currency (TRY, USD, EUR) │ └── roles: internalNote (admin:allow, customer:deny), auditLog (auditor:allow) │ -├── [updateData] -│ └── update-instance-data (Manual, target: $self) +├── [cancel] cancel-with-schema +│ ├── schema: schema-validation-cancel (required: cancelReason min5, cancelledBy) +│ ├── target: cancelled-state +│ └── availableIn: data-initialized, schema-validated-state, no-schema-state, subflow-state +│ +├── [exit] exit-with-schema +│ ├── schema: schema-validation-exit (required: exitReason, exitCode enum) +│ ├── target: exited-state +│ └── availableIn: data-initialized, schema-validated-state, no-schema-state, subflow-state +│ +├── [updateData] update-with-schema (CHILD workflow'da tanimli — schema-validation-subflow-child) +│ ├── schema: schema-validation-update-data (required: notes min3) +│ ├── target: $self (parent instance data guncellenir) +│ └── tetikleme: parent instance SubFlow state'te iken parent ID uzerinden cagrilir │ ├── data-initialized (Initial, stateType:1) │ └── Transitions: -│ ├── confirm-with-schema (Manual) → schema-validated-state -│ │ ├── schema: schema-data-confirm-transition -│ │ │ └── required: confirmed (boolean), confirmedBy (string) -│ │ └── onExecutionTasks: -│ │ └── schema-data-script-task + ConfirmMapping.csx +│ ├── confirm-with-schema (Manual, schema: schema-validation-confirm-transition) → schema-validated-state │ └── skip-to-no-schema (Manual) → no-schema-state │ ├── schema-validated-state (Intermediate, stateType:2) +│ ├── onEntries: +│ │ └── schema-validation-script-task + ConfirmExecutionMapping.csx │ └── Transitions: -│ └── to-no-schema (Manual) → no-schema-state +│ ├── to-no-schema (Manual) → no-schema-state +│ └── enter-subflow (Manual) → subflow-state +│ +├── subflow-state (SubFlow, stateType:4) +│ ├── subFlow.process: schema-validation-subflow-child (type S) +│ └── Transitions: +│ └── subflow-complete (Manual) → completed-state │ ├── no-schema-state (Intermediate, stateType:2) │ └── Transitions: -│ └── complete-schema-test (Manual) → completed-state +│ └── complete-schema-validation-test (Manual) → completed-state │ -└── completed-state (Final/Success, stateType:3, subType:1) +├── completed-state (Final/Success, stateType:3, subType:1) +├── cancelled-state (Final/Cancelled, stateType:3, subType:2) +└── exited-state (Final/Exit, stateType:3, subType:3) ``` ### Test Edilen Ozellikler | Ozellik | Nasil Test Edilir | Ilgili Eleman | |---------|-------------------|---------------| -| Master schema (workflow-level) | Workflow attributes.schema ile tanimlama | schema-data-master | -| Transition schema | Transition'a schema baglama | schema-data-confirm-transition | -| Schema validasyonu | Gecerli data ile transition | confirm-with-schema | -| Schema sessiz reddi | Gecersiz data gonderildiginde state degismez | HTTP test: eksik required alan gonderme | -| updateData ($self) | State degistirmeden data guncelleme | update-instance-data | +| Master schema (workflow-level) | Workflow attributes.schema ile tanimlama | schema-validation-master | +| Start transition schema | startTransition.schema ile baslangic payload dogrulama | schema-validation-start | +| Transition schema (manual) | Transition'a schema baglama, required + type dogrulama | schema-validation-confirm-transition | +| Cancel transition schema | attributes.cancel.schema ile iptal payload dogrulama | schema-validation-cancel | +| Exit transition schema | attributes.exit.schema ile cikis payload dogrulama | schema-validation-exit | +| UpdateData transition schema (SubFlow) | Child workflow'un `attributes.updateData.schema` ile parent data guncelleme dogrulama (child'da tanimli, parent ID uzerinden tetiklenir) | schema-validation-update-data (child: schema-validation-subflow-child) | +| Schema sessiz reddi | Gecersiz data gonderildiginde state degismez | HTTP test: eksik required alan / tip uyumsuzlugu | | Field roles (master schema alan gorunurlugu) | Alan bazli data gorunurlugu | internalNote (admin/customer), auditLog (auditor) | -| JSON Schema Draft 2020-12 | Schema standardi uyumu | Her iki schema dosyasi | -| Required alan kontrolu | Zorunlu alanlarin validasyonu | orderId, customerName, amount, currency | -| Enum validation | Allowed value-set check | currency: TRY, USD, EUR | -| ETag | Instance data versiyonlama (HTTP test) | HTTP test dosyasinda | +| JSON Schema Draft 2020-12 | Schema standardi uyumu | Tum schema dosyalari | +| Required alan kontrolu | Zorunlu alanlarin validasyonu | orderId, customerName, amount, currency, cancelReason, notes | +| Enum validation | Allowed value-set check | currency: TRY/USD/EUR, exitCode: success/failure/timeout | +| SubFlow state + updateData | SubFlow icinde updateData ile parent data guncelleme (eTag degisimi + notes alani yazimi) | subflow-state + schema-validation-subflow-child (updateData child'da tanimli) | ### Kullanilan Bilesenler | Tip | Anahtar | Dosya | |-----|---------|-------| -| Task | `schema-data-script-task` (type:7) | Tasks/schema-data/schema-data-script-task.json | -| Schema | `schema-data-master` | Schemas/schema-data/schema-data-master.json | -| Schema | `schema-data-confirm-transition` | Schemas/schema-data/schema-data-confirm-transition.json | -| CSX | SchemaInitMapping | Workflows/schema-data/src/SchemaInitMapping.csx | -| CSX | ConfirmMapping | Workflows/schema-data/src/ConfirmMapping.csx | +| Workflow | `schema-validation-test-workflow` (type:F) | Workflows/schema-validation/schema-validation-test-workflow.json | +| Workflow | `schema-validation-subflow-child` (type:S) | Workflows/schema-validation/schema-validation-subflow-child.json | +| Task | `schema-validation-script-task` (type:7) | Tasks/schema-validation/schema-validation-script-task.json | +| Schema | `schema-validation-master` | Schemas/schema-validation/schema-validation-master.json | +| Schema | `schema-validation-start` | Schemas/schema-validation/schema-validation-start.json | +| Schema | `schema-validation-confirm-transition` | Schemas/schema-validation/schema-validation-confirm-transition.json | +| Schema | `schema-validation-cancel` | Schemas/schema-validation/schema-validation-cancel.json | +| Schema | `schema-validation-exit` | Schemas/schema-validation/schema-validation-exit.json | +| Schema | `schema-validation-update-data` | Schemas/schema-validation/schema-validation-update-data.json | +| CSX | SchemaInitMapping | Workflows/schema-validation/src/SchemaInitMapping.csx | +| CSX | ConfirmExecutionMapping | Workflows/schema-validation/src/ConfirmExecutionMapping.csx | + +### C# Integration Test Dosyalari + +| Dosya | Rol | +|-------|-----| +| `tests/Core/Tests/schema-validation-test-workflow/SchemaValidationTestWorkflowTests.cs` | [Fact] test metotlari (18 test) | +| `tests/Core/Tests/schema-validation-test-workflow/SchemaValidationScenarioActions.cs` | Akis adimlari (start, confirm, skip, complete, cancel, exit, enter-subflow, updateData) | +| `tests/Core/Tests/schema-validation-test-workflow/SchemaValidationInstanceDataAssertions.cs` | Instance data assertion helper | + +### C# Test Senaryolari + +| Test Metodu | Ne Test Eder | +|-------------|-------------| +| `HappyPath_FullChain_CompletesWithStatusC` | Tam zincir: start → confirm → to-no-schema → complete → status C | +| `StartTransition_SetsInitializedStatus` | SchemaInitMapping: orderId, customerName, amount, currency, status=initialized | +| `ConfirmTransition_WithValidSchema_MovesToSchemaValidatedState` | Transition schema gecerli data ile gecis | +| `ConfirmTransition_WithPartialData_DoesNotTransition` | Eksik required alanla (confirmedBy yok) confirm reddedilir | +| `FieldRoles_AdminRole_SeesInternalNote` | admin rolu internalNote gorur | +| `FieldRoles_CustomerRole_DoesNotSeeInternalNote` | customer rolu internalNote goremez | +| `FieldRoles_AuditorRole_SeesAuditLog` | auditor rolu auditLog gorur | +| `FieldRoles_NoRole_DoesNotSeeRoleRestrictedFields` | rol yok — internalNote ve auditLog gizli | +| `NoSchemaTransition_AcceptsAnyBody` | Schema olmayan transition herhangi body kabul eder | +| `MasterSchema_RejectsInstanceWithInvalidEnum` | Master/start schema gecersiz enum (JPY) ile baslatma reddedilir | +| `StartTransitionSchema_RejectsMissingRequiredFields` | Start schema eksik required alanlarla baslatma reddedilir | +| `StartTransitionSchema_AcceptsValidPayload` | Start schema gecerli payload ile basarili gecis | +| `ConfirmTransition_WithInvalidSchemaPayload_DoesNotChangeState` | Transition schema tip uyumsuzlugu ile sessiz red | +| `ConfirmTransition_WithMissingRequiredField_DoesNotChangeState` | Transition schema eksik required alan ile sessiz red | +| `CancelTransition_WithValidSchemaPayload_MovesToCancelled` | Cancel schema gecerli payload ile cancelled-state | +| `CancelTransition_WithMissingCancelReason_DoesNotCancel` | Cancel schema eksik cancelReason ile sessiz red | +| `ExitTransition_WithValidSchemaPayload_MovesToExited` | Exit schema gecerli payload ile exited-state | +| `ExitTransition_WithInvalidExitCodeEnum_DoesNotExit` | Exit schema gecersiz enum ile sessiz red | +| `UpdateData_InSubflow_WithValidSchema_UpdatesAttributes` | SubFlow'da updateData gecerli payload ile parent data guncellenir: eTag degisir + `notes` alani parent attributes'a yazilir | +| `UpdateData_InSubflow_WithMissingNotes_DoesNotUpdate` | SubFlow'da updateData eksik notes ile schema sessiz reddeder: eTag degismez + gecersiz alan parent'a yazilmaz | --- @@ -1313,7 +1373,7 @@ Her grubun hangi vNext ozelliklerini test ettigini gosteren matris. **Gn** sutun | State tipleri (1/2/3) | X | X | X | X | X | X | X | X | X | X | | State alt tipleri (1/2/3) | X | X | | X | | | X | | | X | | **State alt tipleri (4/5/6)** | | | | | | | X | | | | -| SubFlow state (4) | | X | | | | | | | | | +| SubFlow state (4) | | X | | | | X | | | | | | Wizard state (5) | | | | | X | | | | | | | Manuel transition (0) | X | X | | | X | X | X | | X | | | Otomatik transition (1) | X | X | X | X | X | | | X | X | X | @@ -1339,8 +1399,8 @@ Her grubun hangi vNext ozelliklerini test ettigini gosteren matris. **Gn** sutun | **Trigger Transition Task (12)** | | | X | | | | | | | | | **SubProcess Task (14)** | | | X | | | | | | | | | **Get Instances Task (15)** | | | X | | | | | | | | -| Cancel transition | X | X | | | | | | | | | -| **Exit transition (`attributes.exit`)** | X | | | | | | | | | | +| Cancel transition | X | X | | | | X | | | | | +| **Exit transition (`attributes.exit`)** | X | | | | | X | | | | | | **Schedule cancel (manuel)** | X | | | | | | | | | | | **Timer reschedule (self-loop)** | X | | | | | | | | | | | Shared transitions ($self) | | X | | | | | | | | | @@ -1348,9 +1408,13 @@ Her grubun hangi vNext ozelliklerini test ettigini gosteren matris. **Gn** sutun | **SubFlow cancel final states (child/grandchild)** | | X | | | | | | | | | | **Effective state (/functions/state, nested)** | | X | | | | | | | | | | updateData ($self) | | X | | | | X | | | | | -| **UpdateData (SubFlow context)** | | X | | | | | | | | | +| **UpdateData (SubFlow context)** | | X | | | | X | | | | | | Master schema | | | | | | X | | | | | +| **Start transition schema** | | | | | | X | | | | | | Transition schema | | | | | | X | | | | | +| **Cancel transition schema** | | | | | | X | | | | | +| **Exit transition schema** | | | | | | X | | | | | +| **UpdateData transition schema** | | | | | | X | | | | | | Field roles (master schema) | | | | | | X | | | | | | **queryRoles (workflow/state)** | X | | | | | | | | | | | **Transition roles (state fn. list filter)** | X | | | | | | | | | | diff --git a/tests/Core/Tests/schema-validation-test-workflow/SchemaValidationInstanceDataAssertions.cs b/tests/Core/Tests/schema-validation-test-workflow/SchemaValidationInstanceDataAssertions.cs new file mode 100644 index 0000000..de83c8a --- /dev/null +++ b/tests/Core/Tests/schema-validation-test-workflow/SchemaValidationInstanceDataAssertions.cs @@ -0,0 +1,106 @@ +using System.Text.Json; +using Core.IntegrationTests.Helpers; +using Xunit; + +namespace Core.IntegrationTests.Tests.SchemaValidationTestWorkflow; + +/// +/// Instance data (attributes) assertions specific to schema-validation-test-workflow. +/// Validates the contract set by SchemaInitMapping.csx and ConfirmExecutionMapping.csx. +/// +public static class SchemaValidationInstanceDataAssertions +{ + /// + /// After start transition (SchemaInitMapping): orderId, customerName, amount, currency, + /// status="initialized" must be present. + /// Note: internalNote and auditLog are set by the mapping but may be hidden by field roles + /// in responses without appropriate role headers. Those are tested separately in FieldRoles tests. + /// + public static void AssertInitializedAttributes(JsonElement attrs) + { + JsonElementAssertions.AssertPropertyNonEmptyString( + attrs, + "orderId", + "SchemaInitMapping should preserve orderId." + ); + JsonElementAssertions.AssertPropertyNonEmptyString( + attrs, + "customerName", + "SchemaInitMapping should preserve customerName." + ); + JsonElementAssertions.AssertPropertyString( + attrs, + "status", + "initialized", + "SchemaInitMapping should set status='initialized'." + ); + + Assert.True( + attrs.TryGetProperty("amount", out var amountEl) + && amountEl.ValueKind == JsonValueKind.Number, + "SchemaInitMapping should preserve amount as a number." + ); + JsonElementAssertions.AssertPropertyNonEmptyString( + attrs, + "currency", + "SchemaInitMapping should preserve currency." + ); + } + + /// + /// After confirm transition: ConfirmExecutionMapping sets status='confirmed', + /// copies confirmed/confirmedBy from payload, and sets updatedAt. + /// + public static void AssertConfirmedAttributes(JsonElement attrs) + { + JsonElementAssertions.AssertPropertyString( + attrs, + "status", + "confirmed", + "ConfirmExecutionMapping should set status='confirmed'." + ); + JsonElementAssertions.AssertPropertyNonEmptyString( + attrs, + "confirmedBy", + "ConfirmExecutionMapping should preserve confirmedBy from transition payload." + ); + JsonElementAssertions.AssertPropertyNonEmptyString( + attrs, + "updatedAt", + "ConfirmExecutionMapping should set updatedAt timestamp." + ); + + Assert.True( + attrs.TryGetProperty("confirmed", out var confirmedEl) + && confirmedEl.ValueKind == JsonValueKind.True, + "ConfirmExecutionMapping should preserve confirmed=true from payload." + ); + } + + /// + /// Asserts that a specific string property exists in the data body (for field-role tests). + /// + public static void AssertFieldVisible(JsonElement dataBody, string fieldName) + { + Assert.True( + dataBody.TryGetProperty(fieldName, out var el) + && el.ValueKind == JsonValueKind.String + && !string.IsNullOrWhiteSpace(el.GetString()), + $"Field '{fieldName}' should be visible (non-empty string) in functions/data response." + ); + } + + /// + /// Asserts that a specific property does NOT exist in the data body (filtered by field role). + /// + public static void AssertFieldHidden(JsonElement dataBody, string fieldName) + { + var exists = dataBody.TryGetProperty(fieldName, out var el) + && el.ValueKind != JsonValueKind.Null + && el.ValueKind != JsonValueKind.Undefined; + Assert.False( + exists, + $"Field '{fieldName}' should be hidden (absent) in functions/data response for this role." + ); + } +} diff --git a/tests/Core/Tests/schema-validation-test-workflow/SchemaValidationScenarioActions.cs b/tests/Core/Tests/schema-validation-test-workflow/SchemaValidationScenarioActions.cs new file mode 100644 index 0000000..a2a12fa --- /dev/null +++ b/tests/Core/Tests/schema-validation-test-workflow/SchemaValidationScenarioActions.cs @@ -0,0 +1,154 @@ +using Core.IntegrationTests.Helpers; + +namespace Core.IntegrationTests.Tests.SchemaValidationTestWorkflow; + +/// +/// Scenario actions for schema-validation-test-workflow. +/// Encapsulates instance start, state transitions, and full happy-path chain. +/// +public sealed class SchemaValidationScenarioActions +{ + private readonly WorkflowInstanceTestHelper _wf; + private readonly TimeSpan _timeout; + + public SchemaValidationScenarioActions(WorkflowInstanceTestHelper wf, TimeSpan timeout) + { + _wf = wf; + _timeout = timeout; + } + + /// + /// Starts a new instance with valid master-schema data. + /// Instance lands on data-initialized after SchemaInitMapping runs. + /// + public async Task StartWithValidDataAsync( + string? orderId = null, + string? customerName = null, + decimal amount = 1500.50m, + string currency = "TRY" + ) + { + var id = await _wf.StartInstanceIdAsync( + new + { + key = WorkflowInstanceTestHelper.UniqueInstanceKey("schema-validation"), + tags = new[] { "integration-test", "schema-validation", "schema" }, + attributes = new + { + orderId = orderId ?? $"ORD-{Guid.NewGuid():N}", + customerName = customerName ?? "Test Customer", + amount, + currency, + }, + } + ); + await _wf.WaitForStateAsync(id, "data-initialized", _timeout); + return id; + } + + /// + /// Runs confirm-with-schema transition with valid payload. + /// + public async Task ConfirmWithSchemaAsync(string instanceId, string confirmedBy = "test-admin") + { + await _wf.RunTransitionAsync( + instanceId, + "confirm-with-schema", + headers: null, + new { attributes = new { confirmed = true, confirmedBy } } + ); + await _wf.WaitForStateAsync(instanceId, "schema-validated-state", _timeout); + } + + /// + /// Runs skip-to-no-schema transition (schema-less, any body accepted). + /// + public async Task SkipToNoSchemaAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, "skip-to-no-schema", headers: null); + await _wf.WaitForStateAsync(instanceId, "no-schema-state", _timeout); + } + + /// + /// Runs to-no-schema transition from schema-validated-state. + /// + public async Task ToNoSchemaFromValidatedAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, "to-no-schema", headers: null); + await _wf.WaitForStateAsync(instanceId, "no-schema-state", _timeout); + } + + /// + /// Runs complete-schema-validation-test transition from no-schema-state to completed. + /// + public async Task CompleteAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, "complete-schema-validation-test", headers: null); + await _wf.WaitForStateAsync(instanceId, "completed-state", _timeout); + } + + /// + /// Attempts to start an instance with invalid start-schema payload (e.g. missing required fields). + /// Returns the instance ID if runtime silently accepts, or throws HttpRequestException for 4xx rejection. + /// + public async Task StartWithInvalidStartSchemaAsync(object? attributesOverride = null) + { + var id = await _wf.StartInstanceIdAsync( + new + { + key = WorkflowInstanceTestHelper.UniqueInstanceKey("schema-validation-invalid-start"), + tags = new[] { "integration-test", "schema-validation", "invalid-start" }, + attributes = attributesOverride ?? new { customerName = "Missing orderId and amount" }, + } + ); + await Task.Delay(500); + return id; + } + + /// + /// Runs enter-subflow transition from schema-validated-state to subflow-state. + /// Waits for effective state subflow-child-active (child's initial state). + /// + public async Task EnterSubflowAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, "enter-subflow", headers: null); + await _wf.WaitForStateAsync(instanceId, "subflow-child-active", _timeout); + } + + /// + /// Runs update-with-schema (updateData) transition in subflow-state. + /// + public async Task RunUpdateDataAsync(string instanceId, object payload) + { + await _wf.RunTransitionAsync(instanceId, "update-with-schema", headers: null, new { attributes = payload }); + } + + /// + /// Runs cancel-with-schema transition with provided payload. + /// + public async Task CancelWithSchemaAsync(string instanceId, object payload) + { + await _wf.RunTransitionAsync(instanceId, "cancel-with-schema", headers: null, new { attributes = payload }); + } + + /// + /// Runs exit-with-schema transition with provided payload. + /// + public async Task ExitWithSchemaAsync(string instanceId, object payload) + { + await _wf.RunTransitionAsync(instanceId, "exit-with-schema", headers: null, new { attributes = payload }); + } + + /// + /// Full happy path: start → confirm → to-no-schema → complete. + /// Returns instance id. + /// + public async Task RunFullHappyPathAsync() + { + var id = await StartWithValidDataAsync(); + await ConfirmWithSchemaAsync(id); + await ToNoSchemaFromValidatedAsync(id); + await CompleteAsync(id); + return id; + } +} diff --git a/tests/Core/Tests/schema-validation-test-workflow/SchemaValidationTestWorkflowTests.cs b/tests/Core/Tests/schema-validation-test-workflow/SchemaValidationTestWorkflowTests.cs new file mode 100644 index 0000000..1d7230b --- /dev/null +++ b/tests/Core/Tests/schema-validation-test-workflow/SchemaValidationTestWorkflowTests.cs @@ -0,0 +1,443 @@ +using System.Net.Http; +using System.Text.Json; +using Core.IntegrationTests.Helpers; +using Core.IntegrationTests.Infrastructure; + +namespace Core.IntegrationTests.Tests.SchemaValidationTestWorkflow; + +/* +! Bugs: +! Cancel transition not available despite availableIn:[] (Transition:100020) +! Exit transition not available in data-initialized despite availableIn listing it (Transition:100020) +! UpdateData transition not available in SubFlow effective state (Transition:100020) +*/ + +/// +/// Integration tests for schema-validation-test-workflow. +/// Covers: master schema validation, transition schema enforcement (+ silent rejection), +/// field roles (master schema property visibility). +/// +public class SchemaValidationTestWorkflowTests : IntegrationTestBase +{ + private const string WorkflowKey = "schema-validation-test-workflow"; + private static readonly TimeSpan ShortTimeout = TimeSpan.FromSeconds(15); + private readonly WorkflowInstanceTestHelper _wf; + private readonly SchemaValidationScenarioActions _scenario; + + public SchemaValidationTestWorkflowTests(VNextTestEnvironment environment) + : base(environment) + { + _wf = new WorkflowInstanceTestHelper(Api, WorkflowKey); + _scenario = new SchemaValidationScenarioActions(_wf, ShortTimeout); + } + + // ========================================================================= + // A. Happy Path + // ========================================================================= + + [Fact] + public async Task HappyPath_FullChain_CompletesWithStatusC() + { + var instanceId = await _scenario.RunFullHappyPathAsync(); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("C", StateFunctionJson.ExtractStatus(stateBody)); + Assert.Equal("completed-state", StateFunctionJson.ExtractStateName(stateBody)); + } + + // ========================================================================= + // B. Start Transition Mapping — SchemaInitMapping sets expected fields + // ========================================================================= + + [Fact] + public async Task StartTransition_SetsInitializedStatus() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + + var attrs = await _wf.GetAttributesAsync(instanceId); + SchemaValidationInstanceDataAssertions.AssertInitializedAttributes(attrs); + } + + // ========================================================================= + // C. Transition Schema Validation + // ========================================================================= + + [Fact] + public async Task ConfirmTransition_WithValidSchema_MovesToSchemaValidatedState() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + + await _wf.RunTransitionAsync( + instanceId, + "confirm-with-schema", + headers: null, + new { attributes = new { confirmed = true, confirmedBy = "integration-test-user" } } + ); + await _wf.WaitForStateAsync(instanceId, "schema-validated-state", ShortTimeout); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("schema-validated-state", StateFunctionJson.ExtractStateName(stateBody)); + + var attrs = await _wf.GetAttributesAsync(instanceId); + SchemaValidationInstanceDataAssertions.AssertConfirmedAttributes(attrs); + } + + [Fact] + public async Task ConfirmTransition_WithPartialData_DoesNotTransition() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + + var ex = await Assert.ThrowsAsync(() => + _wf.RunTransitionAsync( + instanceId, + "confirm-with-schema", + headers: null, + new { attributes = new { confirmed = true } } + ) + ); + Assert.Contains("schema validation failed", ex.Message, StringComparison.OrdinalIgnoreCase); + + var stateName = await _wf.GetStateNameAsync(instanceId); + Assert.Equal("data-initialized", stateName); + } + + // ========================================================================= + // D. Field Roles (Master Schema property-level visibility) + // ========================================================================= + + [Fact] + public async Task FieldRoles_AdminRole_SeesInternalNote() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + + var dataBody = await _wf.CallFunctionAsync( + instanceId, + "data", + headers: WorkflowTestHttpHeaders.Role("admin") + ); + + var dataContent = ExtractDataContent(dataBody); + SchemaValidationInstanceDataAssertions.AssertFieldVisible(dataContent, "internalNote"); + } + + [Fact] + public async Task FieldRoles_CustomerRole_DoesNotSeeInternalNote() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + + var dataBody = await _wf.CallFunctionAsync( + instanceId, + "data", + headers: WorkflowTestHttpHeaders.Role("customer") + ); + + var dataContent = ExtractDataContent(dataBody); + SchemaValidationInstanceDataAssertions.AssertFieldHidden(dataContent, "internalNote"); + } + + [Fact] + public async Task FieldRoles_AuditorRole_SeesAuditLog() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + + var dataBody = await _wf.CallFunctionAsync( + instanceId, + "data", + headers: WorkflowTestHttpHeaders.Role("auditor") + ); + + var dataContent = ExtractDataContent(dataBody); + SchemaValidationInstanceDataAssertions.AssertFieldVisible(dataContent, "auditLog"); + } + + [Fact] + public async Task FieldRoles_NoRole_DoesNotSeeRoleRestrictedFields() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + + var dataBody = await _wf.CallFunctionAsync(instanceId, "data", headers: null); + + var dataContent = ExtractDataContent(dataBody); + SchemaValidationInstanceDataAssertions.AssertFieldHidden(dataContent, "internalNote"); + SchemaValidationInstanceDataAssertions.AssertFieldHidden(dataContent, "auditLog"); + } + + // ========================================================================= + // E. Schema-less Transition (accepts any body) + // ========================================================================= + + [Fact] + public async Task NoSchemaTransition_AcceptsAnyBody() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + + await _wf.RunTransitionAsync(instanceId, "skip-to-no-schema", headers: null); + await _wf.WaitForStateAsync(instanceId, "no-schema-state", ShortTimeout); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("no-schema-state", StateFunctionJson.ExtractStateName(stateBody)); + } + + // ========================================================================= + // F. Master Schema — Invalid Enum Rejection + // ========================================================================= + + [Fact] + public async Task MasterSchema_RejectsInstanceWithInvalidEnum() + { + try + { + var instanceId = await _scenario.StartWithValidDataAsync(currency: "JPY"); + + var stateName = await _wf.GetStateNameAsync(instanceId); + Assert.Fail( + $"Expected start rejection for invalid currency enum 'JPY', " + + $"but instance was created and reached state '{stateName}'. " + + "Master schema enum validation may not reject at start time." + ); + } + catch (HttpRequestException ex) when (ex.Message.Contains("NotFound")) + { + Assert.Fail( + "Workflow not found in runtime (404). Publish the workflow first, then re-run tests." + ); + } + catch (HttpRequestException) + { + // 4xx rejection — master schema (or start schema) correctly blocked invalid enum + } + } + + // ========================================================================= + // G. Start Transition Schema Validation + // ========================================================================= + + [Fact] + public async Task StartTransitionSchema_RejectsMissingRequiredFields() + { + try + { + var instanceId = await _scenario.StartWithInvalidStartSchemaAsync( + new { customerName = "Only name, missing orderId/amount/currency" } + ); + + var stateName = await _wf.GetStateNameAsync(instanceId); + Assert.Fail( + $"Expected start rejection for missing required fields (orderId, amount, currency), " + + $"but instance was created and reached state '{stateName}'. " + + "Start transition schema validation did not reject the payload." + ); + } + catch (HttpRequestException ex) when (ex.Message.Contains("NotFound")) + { + Assert.Fail( + "Workflow not found in runtime (404). Publish the workflow first, then re-run tests." + ); + } + catch (HttpRequestException) + { + // 4xx rejection — start schema correctly blocked missing required fields + } + } + + [Fact] + public async Task StartTransitionSchema_AcceptsValidPayload() + { + var instanceId = await _scenario.StartWithValidDataAsync( + orderId: "ORD-VALID-001", + customerName: "Valid Customer", + amount: 100.0m, + currency: "USD" + ); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("data-initialized", StateFunctionJson.ExtractStateName(stateBody)); + } + + // ========================================================================= + // H. Manual Transition Schema — Invalid Payload Rejection + // ========================================================================= + + [Fact] + public async Task ConfirmTransition_WithInvalidSchemaPayload_DoesNotChangeState() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + + var ex = await Assert.ThrowsAsync(() => + _wf.RunTransitionAsync( + instanceId, + "confirm-with-schema", + headers: null, + new { attributes = new { confirmed = "yes", confirmedBy = 123 } } + ) + ); + Assert.Contains("schema validation failed", ex.Message, StringComparison.OrdinalIgnoreCase); + + var stateName = await _wf.GetStateNameAsync(instanceId); + Assert.Equal("data-initialized", stateName); + } + + [Fact] + public async Task ConfirmTransition_WithMissingRequiredField_DoesNotChangeState() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + + var ex = await Assert.ThrowsAsync(() => + _wf.RunTransitionAsync( + instanceId, + "confirm-with-schema", + headers: null, + new { attributes = new { confirmed = true } } + ) + ); + Assert.Contains("schema validation failed", ex.Message, StringComparison.OrdinalIgnoreCase); + + var stateName = await _wf.GetStateNameAsync(instanceId); + Assert.Equal("data-initialized", stateName); + } + + // ========================================================================= + // I. Cancel Transition Schema + // ========================================================================= + + [Fact] + public async Task CancelTransition_WithValidSchemaPayload_MovesToCancelled() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + + await _scenario.CancelWithSchemaAsync( + instanceId, + new { cancelReason = "No longer needed", cancelledBy = "test-user" } + ); + + await _wf.WaitForStateAsync(instanceId, "cancelled-state", ShortTimeout); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("cancelled-state", StateFunctionJson.ExtractStateName(stateBody)); + Assert.Equal("C", StateFunctionJson.ExtractStatus(stateBody)); + } + + [Fact] + public async Task CancelTransition_WithMissingCancelReason_DoesNotCancel() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + + await _scenario.CancelWithSchemaAsync(instanceId, new { cancelledBy = "test-user" }); + + await Task.Delay(1000); + + var stateName = await _wf.GetStateNameAsync(instanceId); + Assert.Equal("data-initialized", stateName); + } + + // ========================================================================= + // J. Exit Transition Schema + // ========================================================================= + + [Fact] + public async Task ExitTransition_WithValidSchemaPayload_MovesToExited() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + + await _scenario.ExitWithSchemaAsync( + instanceId, + new { exitReason = "Test completed", exitCode = "success" } + ); + + await _wf.WaitForStateAsync(instanceId, "exited-state", ShortTimeout); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("exited-state", StateFunctionJson.ExtractStateName(stateBody)); + Assert.Equal("C", StateFunctionJson.ExtractStatus(stateBody)); + } + + [Fact] + public async Task ExitTransition_WithInvalidExitCodeEnum_DoesNotExit() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + + await _scenario.ExitWithSchemaAsync( + instanceId, + new { exitReason = "Test", exitCode = "invalid-code" } + ); + + await Task.Delay(1000); + + var stateName = await _wf.GetStateNameAsync(instanceId); + Assert.Equal("data-initialized", stateName); + } + + // ========================================================================= + // K. UpdateData Schema (SubFlow context) + // ========================================================================= + + [Fact] + public async Task UpdateData_InSubflow_WithValidSchema_UpdatesAttributes() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + await _scenario.ConfirmWithSchemaAsync(instanceId); + await _scenario.EnterSubflowAsync(instanceId); + await Task.Delay(2000); + + var bodyBefore = await _wf.GetInstanceBodyAsync(instanceId); + var eTagBefore = GetETag(bodyBefore); + + await _scenario.RunUpdateDataAsync( + instanceId, + new { notes = "Updated via schema-validated updateData" } + ); + + await Task.Delay(1500); + + var bodyAfter = await _wf.GetInstanceBodyAsync(instanceId); + var eTagAfter = GetETag(bodyAfter); + + Assert.NotEqual(eTagBefore, eTagAfter); + + var parentAttrs = await _wf.GetAttributesAsync(instanceId); + Assert.True( + parentAttrs.TryGetProperty("notes", out var notesEl), + "updateData should write 'notes' field to parent instance attributes, but it was not found. " + + "Verify that child workflow's updateData mapping writes the transition payload into parent data." + ); + Assert.Equal("Updated via schema-validated updateData", notesEl.GetString()); + } + + [Fact] + public async Task UpdateData_InSubflow_WithMissingNotes_DoesNotUpdate() + { + var instanceId = await _scenario.StartWithValidDataAsync(); + await _scenario.ConfirmWithSchemaAsync(instanceId); + await _scenario.EnterSubflowAsync(instanceId); + + var ex = await Assert.ThrowsAsync(() => + _scenario.RunUpdateDataAsync(instanceId, new { someOtherField = "not notes" }) + ); + Assert.Contains("schema validation failed", ex.Message, StringComparison.OrdinalIgnoreCase); + + var parentAttrs = await _wf.GetAttributesAsync(instanceId); + Assert.False( + parentAttrs.TryGetProperty("someOtherField", out _), + "updateData with missing required 'notes' field should be rejected by schema validation. " + + "Parent attributes should NOT contain 'someOtherField'." + ); + } + + // ========================================================================= + // Private helpers + // ========================================================================= + + private static JsonElement ExtractDataContent(JsonElement body) + { + if (body.TryGetProperty("data", out var dataEl) && dataEl.ValueKind == JsonValueKind.Object) + return dataEl; + return body; + } + + private static string GetETag(JsonElement attributes) + { + if (attributes.TryGetProperty("eTag", out var eTag)) + return eTag.GetString() ?? ""; + return ""; + } +} From 449a03c0e0272d7753b96d0f0fd8f7dd9a259d65 Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Fri, 8 May 2026 10:35:01 +0300 Subject: [PATCH 20/24] version-consistency test is completed --- .../postman-schema-validation.json | 2757 ++++++++++++++++- .../schema-validation-test-workflow.http | 610 ++-- .../postman-version-consistency.json | 1471 ++++++--- .../version-consistency-test.http | 402 ++- core.link.json | 2 +- .../error-boundary-test-workflow.diagram.json | 3 + ...-consistency-test-workflow-v2.diagram.json | 3 + ...ion-consistency-test-workflow.diagram.json | 3 + ...ersionConsistencyInstanceDataAssertions.cs | 191 ++ .../VersionConsistencyScenarioActions.cs | 95 + .../VersionConsistencyTestWorkflowTests.cs | 280 ++ 11 files changed, 5040 insertions(+), 777 deletions(-) create mode 100644 core/Workflows/error-boundary/.meta/error-boundary-test-workflow.diagram.json create mode 100644 core/Workflows/version-consistency/.meta/version-consistency-test-workflow-v2.diagram.json create mode 100644 core/Workflows/version-consistency/.meta/version-consistency-test-workflow.diagram.json create mode 100644 tests/Core/Tests/version-consistency-test-workflow/VersionConsistencyInstanceDataAssertions.cs create mode 100644 tests/Core/Tests/version-consistency-test-workflow/VersionConsistencyScenarioActions.cs create mode 100644 tests/Core/Tests/version-consistency-test-workflow/VersionConsistencyTestWorkflowTests.cs diff --git a/api-tests/schema-validation/postman-schema-validation.json b/api-tests/schema-validation/postman-schema-validation.json index 5050231..5521d90 100644 --- a/api-tests/schema-validation/postman-schema-validation.json +++ b/api-tests/schema-validation/postman-schema-validation.json @@ -2,282 +2,2801 @@ "info": { "_postman_id": "3f65c974-a6ce-456b-81ca-b47a3e24e9f2", "name": "vNext Test - Schema Validation", - "description": "vNext workflow integration test collection for schema-validation-test-workflow.\nCovers: master schema, transition schema (+ silent rejection), field roles, schema-less transitions.", + "description": "schema-validation-test-workflow.http ile ayni akis.\nKaynak: SchemaValidationTestWorkflowTests.cs\nPATCH body: { \"attributes\": { ... } }", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "variable": [ - { "key": "baseUrl", "value": "http://localhost:4201", "type": "string" }, - { "key": "apiVersion", "value": "1", "type": "string" }, - { "key": "domain", "value": "core", "type": "string" }, - { "key": "workflow", "value": "schema-validation-test-workflow", "type": "string" } + { + "key": "baseUrl", + "value": "http://localhost:4201", + "type": "string" + }, + { + "key": "apiVersion", + "value": "1", + "type": "string" + }, + { + "key": "domain", + "value": "core", + "type": "string" + }, + { + "key": "workflow", + "value": "schema-validation-test-workflow", + "type": "string" + } ], "item": [ { - "name": "TEST 1: Happy Path — Full Chain to Completed", + "name": "HappyPath_FullChain_CompletesWithStatusC", "item": [ { - "name": "Adim 1: Workflow Baslat", + "name": "HP.1 Start", "request": { "method": "POST", - "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", - "host": ["{{baseUrl}}"], - "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], - "query": [{ "key": "sync", "value": "true" }] + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] }, "body": { "mode": "raw", - "raw": "{\n \"key\": \"schema-validation-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-validation\", \"schema\"],\n \"attributes\": {\n \"orderId\": \"ORD-001\",\n \"customerName\": \"Test Customer\",\n \"amount\": 1500.50,\n \"currency\": \"TRY\"\n }\n}", - "options": { "raw": { "language": "json" } } + "raw": "{\n \"key\": \"schema-happy-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\",\n \"schema\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-001\",\n \"customerName\": \"Test Customer\",\n \"amount\": 1500.5,\n \"currency\": \"TRY\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } } }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["pm.test('Status code is 200', function () { pm.response.to.have.status(200); });", "var jsonData = pm.response.json();", "pm.test('Response has id', function () { pm.expect(jsonData.id).to.be.a('string'); });", "if (jsonData && jsonData.id) { pm.collectionVariables.set('schemaInstanceId', jsonData.id); }"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.response.to.have.status(200);", + "var jsonData = pm.response.json();", + "pm.collectionVariables.set('happyInstanceId', jsonData.id);" + ] + } + } + ] }, { - "name": "Adim 2: Durum Kontrol (data-initialized)", - "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{schemaInstanceId}}", "functions", "state"] } }, - "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State is data-initialized', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('data-initialized'); });"] } }] + "name": "HP.2 State data-initialized", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{happyInstanceId}}", + "functions", + "state" + ] + } + }, + "response": [] }, { - "name": "Adim 3: Transition Schema ile Onayla", - "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/confirm-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{schemaInstanceId}}", "transitions", "confirm-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"confirmed\": true,\n \"confirmedBy\": \"test-admin\"\n}", "options": { "raw": { "language": "json" } } } }, + "name": "HP.3 confirm-with-schema", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyInstanceId}}/transitions/confirm-with-schema?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{happyInstanceId}}", + "transitions", + "confirm-with-schema" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"attributes\": {\n \"confirmed\": true,\n \"confirmedBy\": \"test-admin\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [] }, { - "name": "Adim 4: Durum Kontrol (schema-validated-state)", - "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{schemaInstanceId}}", "functions", "state"] } }, + "name": "HP.4 State schema-validated-state", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{happyInstanceId}}", + "functions", + "state" + ] + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State is schema-validated-state', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('schema-validated-state'); });"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var st = jsonData.state || (jsonData.data && jsonData.data.state) || '';", + "pm.test('schema-validated-state', () => pm.expect(st).to.eql('schema-validated-state'));" + ] + } + } + ] }, { - "name": "Adim 5: No-schema state'e gec", - "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/to-no-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{schemaInstanceId}}", "transitions", "to-no-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } }, + "name": "HP.5 to-no-schema", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyInstanceId}}/transitions/to-no-schema?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{happyInstanceId}}", + "transitions", + "to-no-schema" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [] }, { - "name": "Adim 6: Tamamla", - "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/complete-schema-validation-test?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{schemaInstanceId}}", "transitions", "complete-schema-validation-test"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } }, + "name": "HP.6 complete-schema-validation-test", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyInstanceId}}/transitions/complete-schema-validation-test?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{happyInstanceId}}", + "transitions", + "complete-schema-validation-test" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [] }, { - "name": "Adim 7: Final Durum Kontrol (completed-state, status C)", - "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{schemaInstanceId}}", "functions", "state"] } }, + "name": "HP.7 completed-state C", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{happyInstanceId}}", + "functions", + "state" + ] + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var st = jsonData.state || (jsonData.data && jsonData.data.state) || '';", + "var stat = jsonData.status || (jsonData.data && jsonData.data.status);", + "pm.test('completed-state', () => pm.expect(st).to.eql('completed-state'));", + "pm.test('status C', () => pm.expect(stat).to.eql('C'));" + ] + } + } + ] + } + ] + }, + { + "name": "StartTransition_SetsInitializedStatus", + "item": [ + { + "name": "ST.1 Start", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-init-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\",\n \"schema\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-INIT\",\n \"customerName\": \"Test Customer\",\n \"amount\": 1500.5,\n \"currency\": \"TRY\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.response.to.have.status(200);", + "var j = pm.response.json();", + "pm.collectionVariables.set('initInstanceId', j.id);" + ] + } + } + ] + }, + { + "name": "ST.2 GET instance (attributes)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{initInstanceId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{initInstanceId}}" + ] + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State is completed-state', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('completed-state'); });", "pm.test('Status is C', function () { pm.expect(jsonData.status || (jsonData.data && jsonData.data.status)).to.equal('C'); });"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.response.to.have.status(200);", + "var j = pm.response.json();", + "pm.test('status initialized', () => pm.expect(j.attributes.status).to.eql('initialized'));", + "pm.test('orderId', () => pm.expect(j.attributes.orderId).to.eql('ORD-INIT'));" + ] + } + } + ] } ] }, { - "name": "TEST 2: Transition Schema Rejection", + "name": "ConfirmTransition_WithValidSchema_MovesToSchemaValidatedState", "item": [ { - "name": "Adim 1: Yeni Instance Baslat", - "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-reject-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema\", \"rejection\"],\n \"attributes\": {\n \"orderId\": \"ORD-002\",\n \"customerName\": \"Reject Test\",\n \"amount\": 500,\n \"currency\": \"USD\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "name": "CV.1 Start", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-valid-confirm-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-CV\",\n \"customerName\": \"Test Customer\",\n \"amount\": 100,\n \"currency\": \"USD\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "if (jsonData && jsonData.id) { pm.collectionVariables.set('rejectInstanceId', jsonData.id); }"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.response.to.have.status(200);", + "pm.collectionVariables.set('validConfirmInstanceId', pm.response.json().id);" + ] + } + } + ] }, { - "name": "Adim 2: YANLIS data gonder", - "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/transitions/confirm-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{rejectInstanceId}}", "transitions", "confirm-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"wrongField\": \"this does not match schema\"\n}", "options": { "raw": { "language": "json" } } } }, + "name": "CV.2 confirm", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{validConfirmInstanceId}}/transitions/confirm-with-schema?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{validConfirmInstanceId}}", + "transitions", + "confirm-with-schema" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"attributes\": {\n \"confirmed\": true,\n \"confirmedBy\": \"integration-test-user\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [] }, { - "name": "Adim 3: Durum Kontrol (HALA data-initialized)", - "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{rejectInstanceId}}", "functions", "state"] } }, + "name": "CV.3 State schema-validated", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{validConfirmInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{validConfirmInstanceId}}", + "functions", + "state" + ] + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var st = jsonData.state || (jsonData.data && jsonData.data.state) || '';", + "pm.test('state', () => pm.expect(st).to.eql('schema-validated-state'));" + ] + } + } + ] + }, + { + "name": "CV.4 GET instance confirmed", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{validConfirmInstanceId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{validConfirmInstanceId}}" + ] + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State unchanged after invalid data', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('data-initialized'); });"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var j = pm.response.json();", + "pm.test('status confirmed', () => pm.expect(j.attributes.status).to.eql('confirmed'));", + "pm.test('confirmedBy', () => pm.expect(j.attributes.confirmedBy).to.eql('integration-test-user'));", + "pm.test('updatedAt', () => pm.expect(j.attributes.updatedAt).to.be.a('string'));" + ] + } + } + ] } ] }, { - "name": "TEST 3: Master Schema Violation at Start", + "name": "ConfirmTransition_WithPartialData_DoesNotTransition", "item": [ { - "name": "Adim 1: Eksik required alan ile baslat", - "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-violation-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema\", \"violation\"],\n \"attributes\": {\n \"orderId\": \"ORD-004\",\n \"customerName\": \"Missing Currency\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "name": "PD.1 Start", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-partial-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-PD\",\n \"customerName\": \"Partial Test\",\n \"amount\": 50,\n \"currency\": \"USD\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.collectionVariables.set('partialInstanceId', pm.response.json().id);" + ] + } + } + ] + }, + { + "name": "PD.2 confirm partial (4xx)", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{partialInstanceId}}/transitions/confirm-with-schema?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{partialInstanceId}}", + "transitions", + "confirm-with-schema" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"attributes\": {\n \"confirmed\": true\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('schema fail status', () => {", + " pm.expect([400, 422, 428]).to.include(pm.response.code);", + "});" + ] + } + } + ] + }, + { + "name": "PD.3 still data-initialized", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{partialInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{partialInstanceId}}", + "functions", + "state" + ] + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["pm.test('Master schema violation returns 400', function () { pm.expect(pm.response.code === 400 || pm.response.code === 200).to.be.true; });"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var st = jsonData.state || (jsonData.data && jsonData.data.state) || '';", + "pm.test('state', () => pm.expect(st).to.eql('data-initialized'));" + ] + } + } + ] } ] }, { - "name": "TEST 4: Field Roles", + "name": "FieldRoles (admin / customer / auditor / none)", "item": [ { - "name": "Adim 1: Instance baslat", - "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-fieldroles-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema\", \"field-roles\"],\n \"attributes\": {\n \"orderId\": \"ORD-005\",\n \"customerName\": \"Field Roles Test\",\n \"amount\": 1000,\n \"currency\": \"TRY\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "name": "FR.1 Start", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-fr-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema\",\n \"field-roles\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-FR\",\n \"customerName\": \"Field Roles Test\",\n \"amount\": 1000,\n \"currency\": \"TRY\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "if (jsonData && jsonData.id) { pm.collectionVariables.set('fieldRolesInstanceId', jsonData.id); }"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.collectionVariables.set('frInstanceId', pm.response.json().id);" + ] + } + } + ] }, { - "name": "Adim 2: admin rolu ile data oku", - "request": { "method": "GET", "header": [{ "key": "role", "value": "admin", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{fieldRolesInstanceId}}", "functions", "data"] } }, + "name": "FR-A admin sees internalNote", + "request": { + "method": "GET", + "header": [ + { + "key": "role", + "value": "admin", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{frInstanceId}}/functions/data", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{frInstanceId}}", + "functions", + "data" + ] + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "var data = jsonData.data || jsonData;", "pm.test('admin sees internalNote', function () { pm.expect(data.internalNote).to.be.a('string').and.not.empty; });"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var d = jsonData.data || jsonData;", + "pm.test('internalNote', () => pm.expect(d.internalNote).to.be.a('string').and.not.empty);" + ] + } + } + ] }, { - "name": "Adim 3: customer rolu ile data oku", - "request": { "method": "GET", "header": [{ "key": "role", "value": "customer", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{fieldRolesInstanceId}}", "functions", "data"] } }, + "name": "FR-C customer hides internalNote", + "request": { + "method": "GET", + "header": [ + { + "key": "role", + "value": "customer", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{frInstanceId}}/functions/data", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{frInstanceId}}", + "functions", + "data" + ] + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "var data = jsonData.data || jsonData;", "pm.test('customer does NOT see internalNote', function () { pm.expect(data.internalNote).to.be.undefined; });"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var d = jsonData.data || jsonData;", + "pm.test('no internalNote', () => pm.expect(d.internalNote).to.be.undefined);" + ] + } + } + ] }, { - "name": "Adim 4: auditor rolu ile data oku", - "request": { "method": "GET", "header": [{ "key": "role", "value": "auditor", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{fieldRolesInstanceId}}", "functions", "data"] } }, + "name": "FR-U auditor sees auditLog", + "request": { + "method": "GET", + "header": [ + { + "key": "role", + "value": "auditor", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{frInstanceId}}/functions/data", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{frInstanceId}}", + "functions", + "data" + ] + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "var data = jsonData.data || jsonData;", "pm.test('auditor sees auditLog', function () { pm.expect(data.auditLog).to.be.a('string').and.not.empty; });"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var d = jsonData.data || jsonData;", + "pm.test('auditLog', () => pm.expect(d.auditLog).to.be.a('string').and.not.empty);" + ] + } + } + ] }, { - "name": "Adim 5: rol belirtmeden data oku", - "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{fieldRolesInstanceId}}", "functions", "data"] } }, + "name": "FR-N no role hides both", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{frInstanceId}}/functions/data", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{frInstanceId}}", + "functions", + "data" + ] + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "var data = jsonData.data || jsonData;", "pm.test('no role does NOT see internalNote', function () { pm.expect(data.internalNote).to.be.undefined; });", "pm.test('no role does NOT see auditLog', function () { pm.expect(data.auditLog).to.be.undefined; });"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var d = jsonData.data || jsonData;", + "pm.test('no internalNote', () => pm.expect(d.internalNote).to.be.undefined);", + "pm.test('no auditLog', () => pm.expect(d.auditLog).to.be.undefined);" + ] + } + } + ] } ] }, { - "name": "TEST 5: Schema-siz Transition", + "name": "NoSchemaTransition_AcceptsAnyBody", "item": [ { - "name": "Adim 1: Instance baslat", - "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-validation-noschema-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-validation\", \"schema\", \"no-schema\"],\n \"attributes\": {\n \"orderId\": \"ORD-007\",\n \"customerName\": \"No Schema Test\",\n \"amount\": 300,\n \"currency\": \"EUR\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "name": "NS.1 Start", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-noschema-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\",\n \"no-schema\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-NS\",\n \"customerName\": \"No Schema Test\",\n \"amount\": 300,\n \"currency\": \"EUR\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "if (jsonData && jsonData.id) { pm.collectionVariables.set('noSchemaInstanceId', jsonData.id); }"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.collectionVariables.set('noSchemaOnlyId', pm.response.json().id);" + ] + } + } + ] }, { - "name": "Adim 2: skip-to-no-schema", - "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/transitions/skip-to-no-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{noSchemaInstanceId}}", "transitions", "skip-to-no-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } }, + "name": "NS.2 skip-to-no-schema", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaOnlyId}}/transitions/skip-to-no-schema?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{noSchemaOnlyId}}", + "transitions", + "skip-to-no-schema" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [] }, { - "name": "Adim 3: Durum Kontrol (no-schema-state)", - "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{noSchemaInstanceId}}", "functions", "state"] } }, + "name": "NS.3 no-schema-state", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaOnlyId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{noSchemaOnlyId}}", + "functions", + "state" + ] + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var st = jsonData.state || (jsonData.data && jsonData.data.state) || '';", + "pm.test('state', () => pm.expect(st).to.eql('no-schema-state'));" + ] + } + } + ] + } + ] + }, + { + "name": "MasterSchema_RejectsInstanceWithInvalidEnum", + "item": [ + { + "name": "MS Start currency JPY", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-master-enum-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\",\n \"master-enum\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-JPY\",\n \"customerName\": \"Enum Test\",\n \"amount\": 100,\n \"currency\": \"JPY\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('rejects or accepts depending on runtime', () => {", + " pm.expect(pm.response.code).to.be.oneOf([200, 400, 404, 422]);", + "});" + ] + } + } + ] + } + ] + }, + { + "name": "StartTransitionSchema_RejectsMissingRequiredFields", + "item": [ + { + "name": "SSR invalid start", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-start-reject-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\",\n \"start-schema-reject\"\n ],\n \"attributes\": {\n \"customerName\": \"Only name, missing orderId/amount/currency\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('4xx expected', () => {", + " pm.expect([400, 404, 422]).to.include(pm.response.code);", + "});" + ] + } + } + ] + } + ] + }, + { + "name": "StartTransitionSchema_AcceptsValidPayload", + "item": [ + { + "name": "SV Start", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-valid-start-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-VALID-001\",\n \"customerName\": \"Valid Customer\",\n \"amount\": 100,\n \"currency\": \"USD\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State is no-schema-state', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('no-schema-state'); });"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.response.to.have.status(200);", + "pm.collectionVariables.set('startValidInstanceId', pm.response.json().id);" + ] + } + } + ] }, { - "name": "Adim 4: complete-schema-validation-test", - "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/transitions/complete-schema-validation-test?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{noSchemaInstanceId}}", "transitions", "complete-schema-validation-test"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } }, - "response": [] + "name": "SV State data-initialized", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{startValidInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{startValidInstanceId}}", + "functions", + "state" + ] + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var st = jsonData.state || (jsonData.data && jsonData.data.state) || '';", + "pm.test('state', () => pm.expect(st).to.eql('data-initialized'));" + ] + } + } + ] + } + ] + }, + { + "name": "ConfirmTransition_WithInvalidSchemaPayload_DoesNotChangeState", + "item": [ + { + "name": "INV.1 Start", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-inv-type-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-INV\",\n \"customerName\": \"Invalid Type Test\",\n \"amount\": 250,\n \"currency\": \"EUR\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.collectionVariables.set('invalidTypeInstanceId', pm.response.json().id);" + ] + } + } + ] + }, + { + "name": "INV.2 wrong types", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{invalidTypeInstanceId}}/transitions/confirm-with-schema?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{invalidTypeInstanceId}}", + "transitions", + "confirm-with-schema" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"attributes\": {\n \"confirmed\": \"yes\",\n \"confirmedBy\": 123\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('4xx', () => pm.expect([400, 422]).to.include(pm.response.code));" + ] + } + } + ] }, { - "name": "Adim 5: Final Durum (completed-state, status C)", - "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{noSchemaInstanceId}}", "functions", "state"] } }, + "name": "INV.3 data-initialized", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{invalidTypeInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{invalidTypeInstanceId}}", + "functions", + "state" + ] + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State is completed-state', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('completed-state'); });", "pm.test('Status is C', function () { pm.expect(jsonData.status || (jsonData.data && jsonData.data.status)).to.equal('C'); });"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var st = jsonData.state || (jsonData.data && jsonData.data.state) || '';", + "pm.test('state', () => pm.expect(st).to.eql('data-initialized'));" + ] + } + } + ] } ] }, { - "name": "TEST 6: Start Transition Schema Rejection", + "name": "ConfirmTransition_WithMissingRequiredField_DoesNotChangeState", "item": [ { - "name": "Adim 1: Eksik required alanlarla baslat (orderId, amount, currency yok)", - "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-start-reject-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-validation\", \"start-schema-reject\"],\n \"attributes\": {\n \"customerName\": \"Only name provided\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "name": "MIS.1 Start", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-missing-cb-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-MIS\",\n \"customerName\": \"Missing CB Test\",\n \"amount\": 1,\n \"currency\": \"USD\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.collectionVariables.set('missingCbInstanceId', pm.response.json().id);" + ] + } + } + ] + }, + { + "name": "MIS.2 confirm without confirmedBy", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{missingCbInstanceId}}/transitions/confirm-with-schema?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{missingCbInstanceId}}", + "transitions", + "confirm-with-schema" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"attributes\": {\n \"confirmed\": true\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('4xx', () => pm.expect([400, 422]).to.include(pm.response.code));" + ] + } + } + ] + }, + { + "name": "MIS.3 data-initialized", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{missingCbInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{missingCbInstanceId}}", + "functions", + "state" + ] + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["pm.test('Start schema rejection returns 400', function () { pm.expect(pm.response.code).to.equal(400); });"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var st = jsonData.state || (jsonData.data && jsonData.data.state) || '';", + "pm.test('state', () => pm.expect(st).to.eql('data-initialized'));" + ] + } + } + ] } ] }, { - "name": "TEST 7: Cancel Transition Schema", + "name": "CancelTransition_WithValidSchemaPayload_MovesToCancelled", "item": [ { - "name": "Adim 1: Instance Baslat", - "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-cancel-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-validation\", \"cancel-schema\"],\n \"attributes\": {\n \"orderId\": \"ORD-CANCEL-001\",\n \"customerName\": \"Cancel Test\",\n \"amount\": 500,\n \"currency\": \"TRY\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "name": "CA.1 Start", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-cancel-ok-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\",\n \"cancel\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-CANCEL-OK\",\n \"customerName\": \"Cancel OK\",\n \"amount\": 500,\n \"currency\": \"TRY\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "if (jsonData && jsonData.id) { pm.collectionVariables.set('cancelInstanceId', jsonData.id); }"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.collectionVariables.set('cancelOkId', pm.response.json().id);" + ] + } + } + ] }, { - "name": "Adim 2: Cancel eksik cancelReason — reddedilmeli", - "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/transitions/cancel-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{cancelInstanceId}}", "transitions", "cancel-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"cancelledBy\": \"test-user\"\n}", "options": { "raw": { "language": "json" } } } }, + "name": "CA.2 cancel valid", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelOkId}}/transitions/cancel-with-schema?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{cancelOkId}}", + "transitions", + "cancel-with-schema" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"attributes\": {\n \"cancelReason\": \"No longer needed\",\n \"cancelledBy\": \"test-user\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [] }, { - "name": "Adim 3: Durum Kontrol (HALA data-initialized)", - "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{cancelInstanceId}}", "functions", "state"] } }, + "name": "CA.3 cancelled-state", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelOkId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{cancelOkId}}", + "functions", + "state" + ] + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var st = jsonData.state || (jsonData.data && jsonData.data.state) || '';", + "var stat = jsonData.status || (jsonData.data && jsonData.data.status);", + "pm.test('cancelled-state', () => pm.expect(st).to.eql('cancelled-state'));", + "pm.test('C', () => pm.expect(stat).to.eql('C'));" + ] + } + } + ] + } + ] + }, + { + "name": "CancelTransition_WithMissingCancelReason_DoesNotCancel", + "item": [ + { + "name": "CB.1 Start", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-cancel-bad-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\",\n \"cancel\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-CANCEL-BAD\",\n \"customerName\": \"Cancel Bad\",\n \"amount\": 500,\n \"currency\": \"TRY\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State unchanged after invalid cancel', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('data-initialized'); });"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.collectionVariables.set('cancelBadId', pm.response.json().id);" + ] + } + } + ] }, { - "name": "Adim 4: Cancel gecerli payload", - "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/transitions/cancel-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{cancelInstanceId}}", "transitions", "cancel-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"cancelReason\": \"No longer needed for testing\",\n \"cancelledBy\": \"test-admin\"\n}", "options": { "raw": { "language": "json" } } } }, + "name": "CB.2 cancel missing reason", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelBadId}}/transitions/cancel-with-schema?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{cancelBadId}}", + "transitions", + "cancel-with-schema" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"attributes\": {\n \"cancelledBy\": \"test-user\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [] }, { - "name": "Adim 5: Durum Kontrol (cancelled-state, status C)", - "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{cancelInstanceId}}", "functions", "state"] } }, + "name": "CB.3 data-initialized", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelBadId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{cancelBadId}}", + "functions", + "state" + ] + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State is cancelled-state', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('cancelled-state'); });", "pm.test('Status is C', function () { pm.expect(jsonData.status || (jsonData.data && jsonData.data.status)).to.equal('C'); });"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var st = jsonData.state || (jsonData.data && jsonData.data.state) || '';", + "pm.test('state', () => pm.expect(st).to.eql('data-initialized'));" + ] + } + } + ] } ] }, { - "name": "TEST 8: Exit Transition Schema", + "name": "ExitTransition_WithValidSchemaPayload_MovesToExited", "item": [ { - "name": "Adim 1: Instance Baslat", - "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-exit-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-validation\", \"exit-schema\"],\n \"attributes\": {\n \"orderId\": \"ORD-EXIT-001\",\n \"customerName\": \"Exit Test\",\n \"amount\": 750,\n \"currency\": \"USD\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "name": "EX.1 Start", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-exit-ok-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\",\n \"exit\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-EXIT-OK\",\n \"customerName\": \"Exit OK\",\n \"amount\": 750,\n \"currency\": \"USD\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "if (jsonData && jsonData.id) { pm.collectionVariables.set('exitInstanceId', jsonData.id); }"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.collectionVariables.set('exitOkId', pm.response.json().id);" + ] + } + } + ] }, { - "name": "Adim 2: Exit gecersiz exitCode enum — reddedilmeli", - "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/transitions/exit-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{exitInstanceId}}", "transitions", "exit-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"exitReason\": \"Testing invalid code\",\n \"exitCode\": \"invalid-code\"\n}", "options": { "raw": { "language": "json" } } } }, + "name": "EX.2 exit valid", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitOkId}}/transitions/exit-with-schema?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{exitOkId}}", + "transitions", + "exit-with-schema" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"attributes\": {\n \"exitReason\": \"Test completed\",\n \"exitCode\": \"success\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [] }, { - "name": "Adim 3: Durum Kontrol (HALA data-initialized)", - "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{exitInstanceId}}", "functions", "state"] } }, + "name": "EX.3 exited-state", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitOkId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{exitOkId}}", + "functions", + "state" + ] + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State unchanged after invalid exit', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('data-initialized'); });"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var st = jsonData.state || (jsonData.data && jsonData.data.state) || '';", + "var stat = jsonData.status || (jsonData.data && jsonData.data.status);", + "pm.test('exited-state', () => pm.expect(st).to.eql('exited-state'));", + "pm.test('C', () => pm.expect(stat).to.eql('C'));" + ] + } + } + ] + } + ] + }, + { + "name": "ExitTransition_WithInvalidExitCodeEnum_DoesNotExit", + "item": [ + { + "name": "EB.1 Start", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-exit-bad-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\",\n \"exit\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-EXIT-BAD\",\n \"customerName\": \"Exit Bad\",\n \"amount\": 1,\n \"currency\": \"USD\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.collectionVariables.set('exitBadId', pm.response.json().id);" + ] + } + } + ] }, { - "name": "Adim 4: Exit gecerli payload", - "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/transitions/exit-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{exitInstanceId}}", "transitions", "exit-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"exitReason\": \"Test completed successfully\",\n \"exitCode\": \"success\"\n}", "options": { "raw": { "language": "json" } } } }, + "name": "EB.2 invalid exitCode", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitBadId}}/transitions/exit-with-schema?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{exitBadId}}", + "transitions", + "exit-with-schema" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"attributes\": {\n \"exitReason\": \"Test\",\n \"exitCode\": \"invalid-code\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [] }, { - "name": "Adim 5: Durum Kontrol (exited-state, status C)", - "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/functions/state", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{exitInstanceId}}", "functions", "state"] } }, + "name": "EB.3 data-initialized", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitBadId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{exitBadId}}", + "functions", + "state" + ] + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "pm.test('State is exited-state', function () { pm.expect(jsonData.state || (jsonData.data && jsonData.data.state)).to.equal('exited-state'); });", "pm.test('Status is C', function () { pm.expect(jsonData.status || (jsonData.data && jsonData.data.status)).to.equal('C'); });"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "var st = jsonData.state || (jsonData.data && jsonData.data.state) || '';", + "pm.test('state', () => pm.expect(st).to.eql('data-initialized'));" + ] + } + } + ] } ] }, { - "name": "TEST 9: SubFlow + UpdateData Schema", + "name": "UpdateData_InSubflow_WithValidSchema_UpdatesAttributes", "item": [ { - "name": "Adim 1: Instance Baslat", - "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"key\": \"schema-subflow-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"schema-validation\", \"subflow-updatedata\"],\n \"attributes\": {\n \"orderId\": \"ORD-SUBFLOW-001\",\n \"customerName\": \"SubFlow Test\",\n \"amount\": 2000,\n \"currency\": \"TRY\"\n }\n}", "options": { "raw": { "language": "json" } } } }, + "name": "UD.1 Start", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-ud-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\",\n \"subflow\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-UD\",\n \"customerName\": \"SubFlow Test\",\n \"amount\": 2000,\n \"currency\": \"TRY\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [], - "event": [{ "listen": "test", "script": { "type": "text/javascript", "exec": ["var jsonData = pm.response.json();", "if (jsonData && jsonData.id) { pm.collectionVariables.set('subflowInstanceId', jsonData.id); }"] } }] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.collectionVariables.set('udInstanceId', pm.response.json().id);" + ] + } + } + ] }, { - "name": "Adim 2: confirm-with-schema", - "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/confirm-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{subflowInstanceId}}", "transitions", "confirm-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"confirmed\": true,\n \"confirmedBy\": \"test-admin\"\n}", "options": { "raw": { "language": "json" } } } }, + "name": "UD.2 confirm", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udInstanceId}}/transitions/confirm-with-schema?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{udInstanceId}}", + "transitions", + "confirm-with-schema" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"attributes\": {\n \"confirmed\": true,\n \"confirmedBy\": \"test-admin\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [] }, { - "name": "Adim 3: enter-subflow", - "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/enter-subflow?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{subflowInstanceId}}", "transitions", "enter-subflow"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } }, + "name": "UD.3 enter-subflow", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udInstanceId}}/transitions/enter-subflow?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{udInstanceId}}", + "transitions", + "enter-subflow" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [] }, { - "name": "Adim 4: UpdateData eksik notes — reddedilmeli", - "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/update-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{subflowInstanceId}}", "transitions", "update-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"someOtherField\": \"not notes\"\n}", "options": { "raw": { "language": "json" } } } }, + "name": "UD.4 state (optional poll)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udInstanceId}}/functions/state", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{udInstanceId}}", + "functions", + "state" + ] + } + }, "response": [] }, { - "name": "Adim 5: UpdateData gecerli payload", - "request": { "method": "PATCH", "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/update-with-schema?sync=true", "host": ["{{baseUrl}}"], "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{subflowInstanceId}}", "transitions", "update-with-schema"], "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", "raw": "{\n \"notes\": \"Valid update data notes\"\n}", "options": { "raw": { "language": "json" } } } }, + "name": "UD.5 eTag before", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udInstanceId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{udInstanceId}}" + ] + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var j = pm.response.json();", + "pm.collectionVariables.set('udEtagBefore', j.eTag || '');" + ] + } + } + ] + }, + { + "name": "UD.6 update-with-schema", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udInstanceId}}/transitions/update-with-schema?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{udInstanceId}}", + "transitions", + "update-with-schema" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"attributes\": {\n \"notes\": \"Updated via schema-validated updateData\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, "response": [] + }, + { + "name": "UD.7 instance notes + eTag", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udInstanceId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{udInstanceId}}" + ] + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var j = pm.response.json();", + "pm.test('notes', () => pm.expect(j.attributes.notes).to.eql('Updated via schema-validated updateData'));", + "var before = pm.collectionVariables.get('udEtagBefore');", + "pm.test('eTag changed if present', () => {", + " if (before && j.eTag) pm.expect(j.eTag).to.not.eql(before);", + "});" + ] + } + } + ] + } + ] + }, + { + "name": "UpdateData_InSubflow_WithMissingNotes_DoesNotUpdate", + "item": [ + { + "name": "UD-B.1 Start", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"schema-ud-bad-{{$timestamp}}\",\n \"tags\": [\n \"integration-test\",\n \"schema-validation\",\n \"subflow\"\n ],\n \"attributes\": {\n \"orderId\": \"ORD-UDBAD\",\n \"customerName\": \"SubFlow Bad\",\n \"amount\": 2000,\n \"currency\": \"TRY\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.collectionVariables.set('udBadInstanceId', pm.response.json().id);" + ] + } + } + ] + }, + { + "name": "UD-B.2 confirm", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udBadInstanceId}}/transitions/confirm-with-schema?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{udBadInstanceId}}", + "transitions", + "confirm-with-schema" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"attributes\": {\n \"confirmed\": true,\n \"confirmedBy\": \"test-admin\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [] + }, + { + "name": "UD-B.3 enter-subflow", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udBadInstanceId}}/transitions/enter-subflow?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{udBadInstanceId}}", + "transitions", + "enter-subflow" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [] + }, + { + "name": "UD-B.4 update missing notes", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udBadInstanceId}}/transitions/update-with-schema?sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{udBadInstanceId}}", + "transitions", + "update-with-schema" + ], + "query": [ + { + "key": "sync", + "value": "true" + } + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"attributes\": {\n \"someOtherField\": \"not notes\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('4xx', () => pm.expect([400, 422]).to.include(pm.response.code));" + ] + } + } + ] + }, + { + "name": "UD-B.5 attributes no someOtherField", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udBadInstanceId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{udBadInstanceId}}" + ] + } + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var j = pm.response.json();", + "pm.test('no someOtherField', () => pm.expect(j.attributes).to.not.have.property('someOtherField'));" + ] + } + } + ] } ] } ] -} +} \ No newline at end of file diff --git a/api-tests/schema-validation/schema-validation-test-workflow.http b/api-tests/schema-validation/schema-validation-test-workflow.http index 2fbbac8..6f9cdbc 100644 --- a/api-tests/schema-validation/schema-validation-test-workflow.http +++ b/api-tests/schema-validation/schema-validation-test-workflow.http @@ -4,18 +4,37 @@ @workflow = schema-validation-test-workflow ############################################################################### -# TEST 1: Happy Path — Master Schema + Transition Schema + Completion -# Tests: master schema active, transition schema enforcement, -# onExecutionTasks with mapping, full chain to status C +# schema-validation-test-workflow — API manual / REST Client tests +# +# Kaynak: tests/Core/Tests/schema-validation-test-workflow/SchemaValidationTestWorkflowTests.cs +# Yardimci: SchemaValidationScenarioActions.cs, SchemaValidationInstanceDataAssertions.cs +# +# GECIS BODY: Tum PATCH isteklerinde gecis payload'u { "attributes": { ... } } zarfinda +# olmali (C# WorkflowInstanceTestHelper / RunTransitionAsync ile ayni). +# +# BEKLENTILER (runtime surumune gore): +# - Bazi schema ihlalleri: HTTP 4xx + mesajda "schema validation failed" (C# HttpRequestException). +# - Bazi eski "sessiz red" senaryolari: 200 ama state degismez — dokumantasyona bakin. +# +# Alan adlari (Initialized): orderId, customerName, amount, currency, status="initialized" +# (Initialized tam teyit: GET .../instances/{id} → attributes) ############################################################################### -### Adim 1: Workflow Baslat (master schema required alanlarla) -# @name createSchemaInstance + +# ============================================================================= +# [Fact] HappyPath_FullChain_CompletesWithStatusC +# Senaryo: SchemaValidationScenarioActions.RunFullHappyPathAsync +# start → confirm-with-schema → to-no-schema → complete-schema-validation-test +# Son: completed-state, status C +# ============================================================================= + +### HP.1 Baslat (gecerli master + start schema) +# @name happyStart POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { - "key": "schema-validation-{{$timestamp}}", + "key": "schema-happy-{{$timestamp}}", "tags": ["integration-test", "schema-validation", "schema"], "attributes": { "orderId": "ORD-001", @@ -25,17 +44,13 @@ Content-Type: application/json } } -@schemaInstanceId = {{createSchemaInstance.response.body.$.id}} - -### Adim 2: Durum Kontrol (data-initialized olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state +@happyInstanceId = {{happyStart.response.body.$.id}} -### Adim 3: Instance Data Kontrol (SchemaInitMapping sonrasi) -# status="initialized", internalNote, auditLog alanlari olmali -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/data +### HP.2 State: data-initialized +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyInstanceId}}/functions/state -### Adim 4: Transition Schema ile Onayla (dogru data) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/confirm-with-schema?sync=true +### HP.3 confirm-with-schema (ConfirmWithSchemaAsync → confirmedBy default "test-admin") +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyInstanceId}}/transitions/confirm-with-schema?sync=true Content-Type: application/json { @@ -45,178 +60,244 @@ Content-Type: application/json } } -### Adim 5: Durum Kontrol (schema-validated-state olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state +### HP.4 State: schema-validated-state +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyInstanceId}}/functions/state -### Adim 6: No-schema state'e gec -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/to-no-schema?sync=true +### HP.5 to-no-schema +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyInstanceId}}/transitions/to-no-schema?sync=true Content-Type: application/json {} -### Adim 7: Tamamla -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/transitions/complete-schema-validation-test?sync=true +### HP.6 complete-schema-validation-test +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyInstanceId}}/transitions/complete-schema-validation-test?sync=true Content-Type: application/json {} -### Adim 8: Final Durum Kontrol (completed-state, status: C olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{schemaInstanceId}}/functions/state +### HP.7 Final: completed-state, status C +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyInstanceId}}/functions/state -############################################################################### -# TEST 2: Transition Schema Rejection (Sessiz Red) -# Tests: yanlis data gonderilirse transition 200 doner ama state DEGISMEZ -############################################################################### -### Adim 1: Yeni Instance Baslat -# @name createRejectInstance +# ============================================================================= +# [Fact] StartTransition_SetsInitializedStatus +# Teyit: GET instance → attributes icinde status="initialized", orderId, amount, currency +# ============================================================================= + +### ST.1 Baslat +# @name initStart POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { - "key": "schema-reject-test-{{$timestamp}}", - "tags": ["integration-test", "schema", "rejection"], + "key": "schema-init-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "schema"], "attributes": { - "orderId": "ORD-002", - "customerName": "Reject Test", - "amount": 500, - "currency": "USD" + "orderId": "ORD-INIT", + "customerName": "Test Customer", + "amount": 1500.50, + "currency": "TRY" } } -@rejectInstanceId = {{createRejectInstance.response.body.$.id}} +@initInstanceId = {{initStart.response.body.$.id}} + +### ST.2 Instance (attributes tam JSON — SchemaValidationInstanceDataAssertions ile uyumlu) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{initInstanceId}} + -### Adim 2: Transition Schema ile YANLIS data gonder (confirmed ve confirmedBy required) -# Bu 200 donecek ama state DEGISMEMELI (sessiz red) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/transitions/confirm-with-schema?sync=true +# ============================================================================= +# [Fact] ConfirmTransition_WithValidSchema_MovesToSchemaValidatedState +# confirmedBy: "integration-test-user" (test dosyasindaki gibi) +# ============================================================================= + +### CV.1 Baslat +# @name validConfirmStart +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { + "key": "schema-valid-confirm-{{$timestamp}}", + "tags": ["integration-test", "schema-validation"], "attributes": { - "wrongField": "this does not match schema" + "orderId": "ORD-CV", + "customerName": "Test Customer", + "amount": 100, + "currency": "USD" } } -### Adim 3: Durum Kontrol (HALA data-initialized olmali - state degismemeli) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/functions/state +@validConfirmInstanceId = {{validConfirmStart.response.body.$.id}} -### Adim 4: Eksik required alan ile dene (confirmedBy yok) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/transitions/confirm-with-schema?sync=true +### CV.2 confirm-with-schema (gecerli) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{validConfirmInstanceId}}/transitions/confirm-with-schema?sync=true Content-Type: application/json { "attributes": { - "confirmed": true + "confirmed": true, + "confirmedBy": "integration-test-user" } } -### Adim 5: Durum Kontrol (HALA data-initialized olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{rejectInstanceId}}/functions/state +### CV.3 State: schema-validated-state +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{validConfirmInstanceId}}/functions/state -############################################################################### -# TEST 3: Master Schema Violation at Start -# Tests: master schema zorunlu alan eksik olunca baslatma basarisiz olmali -############################################################################### +### CV.4 attributes: status confirmed, confirmedBy, updatedAt (istege bagli teyit) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{validConfirmInstanceId}} + + +# ============================================================================= +# [Fact] ConfirmTransition_WithPartialData_DoesNotTransition +# Body: { "attributes": { "confirmed": true } } — confirmedBy eksik +# Beklenti: HTTP 4xx; mesajda "schema validation failed"; state data-initialized kalir +# ============================================================================= -### Adim 1: Eksik required alan ile baslat (currency ve amount yok) +### PD.1 Baslat +# @name partialStart POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { - "key": "schema-violation-test-{{$timestamp}}", - "tags": ["integration-test", "schema", "violation"], + "key": "schema-partial-{{$timestamp}}", + "tags": ["integration-test", "schema-validation"], "attributes": { - "orderId": "ORD-004", - "customerName": "Missing Currency" + "orderId": "ORD-PD", + "customerName": "Partial Test", + "amount": 50, + "currency": "USD" } } -############################################################################### -# TEST 4: Field Roles — Master Schema alan bazli gorunurluk -# Tests: role header'a gore internalNote ve auditLog filtrelenir -############################################################################### +@partialInstanceId = {{partialStart.response.body.$.id}} + +### PD.2 confirm — sadece confirmed (4xx beklenir) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{partialInstanceId}}/transitions/confirm-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "confirmed": true + } +} + +### PD.3 State hala data olmali +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{partialInstanceId}}/functions/state -### Adim 1: Instance baslat (field roles testi icin) -# @name createFieldRolesInstance + +# ============================================================================= +# [Fact] FieldRoles_AdminRole_SeesInternalNote +# ============================================================================= + +### FR-A.1 Baslat +# @name frAdminStart POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { - "key": "schema-fieldroles-test-{{$timestamp}}", + "key": "schema-fr-{{$timestamp}}", "tags": ["integration-test", "schema", "field-roles"], "attributes": { - "orderId": "ORD-005", + "orderId": "ORD-FR", "customerName": "Field Roles Test", "amount": 1000, "currency": "TRY" } } -@fieldRolesInstanceId = {{createFieldRolesInstance.response.body.$.id}} +@frInstanceId = {{frAdminStart.response.body.$.id}} -### Adim 2: admin rolu ile data oku — internalNote GORUNMELI -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data +### FR-A.2 GET data, role: admin — internalNote string ve dolu olmali +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{frInstanceId}}/functions/data role: admin -### Adim 3: customer rolu ile data oku — internalNote GORUNMEMELI -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data + +# ============================================================================= +# [Fact] FieldRoles_CustomerRole_DoesNotSeeInternalNote +# ============================================================================= + +### FR-C. GET data, role: customer — internalNote OLMAMALI +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{frInstanceId}}/functions/data role: customer -### Adim 4: auditor rolu ile data oku — auditLog GORUNMELI -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data + +# ============================================================================= +# [Fact] FieldRoles_AuditorRole_SeesAuditLog +# ============================================================================= + +### FR-U. GET data, role: auditor — auditLog string ve dolu olmali +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{frInstanceId}}/functions/data role: auditor -### Adim 5: rol belirtmeden data oku — internalNote ve auditLog GORUNMEMELI -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{fieldRolesInstanceId}}/functions/data -############################################################################### -# TEST 5: Schema-siz Transition (Serbest gecis) -# Tests: schema olmayan transition herhangi body kabul eder -############################################################################### +# ============================================================================= +# [Fact] FieldRoles_NoRole_DoesNotSeeRoleRestrictedFields +# ============================================================================= + +### FR-N. GET data, role yok — internalNote ve auditLog OLMAMALI +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{frInstanceId}}/functions/data + -### Adim 1: Instance baslat -# @name createNoSchemaInstance +# ============================================================================= +# [Fact] NoSchemaTransition_AcceptsAnyBody +# Sadece skip-to-no-schema → no-schema-state (complete yok) +# ============================================================================= + +### NS.1 Baslat +# @name noSchemaStart POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { - "key": "schema-validation-noschema-{{$timestamp}}", - "tags": ["integration-test", "schema-validation", "schema", "no-schema"], + "key": "schema-noschema-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "no-schema"], "attributes": { - "orderId": "ORD-007", + "orderId": "ORD-NS", "customerName": "No Schema Test", "amount": 300, "currency": "EUR" } } -@noSchemaInstanceId = {{createNoSchemaInstance.response.body.$.id}} +@noSchemaOnlyId = {{noSchemaStart.response.body.$.id}} -### Adim 2: skip-to-no-schema (schema yok, bos body ile gecis yapilabilmeli) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/transitions/skip-to-no-schema?sync=true +### NS.2 skip-to-no-schema (bos body) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaOnlyId}}/transitions/skip-to-no-schema?sync=true Content-Type: application/json {} -### Adim 3: Durum Kontrol (no-schema-state olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/functions/state +### NS.3 State: no-schema-state +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaOnlyId}}/functions/state + + +# ============================================================================= +# [Fact] MasterSchema_RejectsInstanceWithInvalidEnum +# currency: JPY (master enum disinda) — baslatma 4xx beklenir (runtime reddederse) +# ============================================================================= -### Adim 4: complete-schema-validation-test ile tamamla -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/transitions/complete-schema-validation-test?sync=true +### MS. Gecersiz enum ile baslat (4xx beklenir) +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json -{} +{ + "key": "schema-master-enum-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "master-enum"], + "attributes": { + "orderId": "ORD-JPY", + "customerName": "Enum Test", + "amount": 100, + "currency": "JPY" + } +} -### Adim 5: Final Durum (completed-state, status: C) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{noSchemaInstanceId}}/functions/state -############################################################################### -# TEST 6: Start Transition Schema Rejection -# Tests: startTransition.schema zorunlu alan eksik olunca baslatma reddedilmeli -############################################################################### +# ============================================================================= +# [Fact] StartTransitionSchema_RejectsMissingRequiredFields +# attributes: sadece customerName (orderId, amount, currency yok) +# ============================================================================= -### Adim 1: Eksik required alan ile baslat (orderId, amount, currency yok) -# startTransition schema: required = orderId, customerName, amount, currency +### SSR. Eksik required ile baslat (4xx beklenir) POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json @@ -224,35 +305,63 @@ Content-Type: application/json "key": "schema-start-reject-{{$timestamp}}", "tags": ["integration-test", "schema-validation", "start-schema-reject"], "attributes": { - "customerName": "Only name provided" + "customerName": "Only name, missing orderId/amount/currency" } } -############################################################################### -# TEST 7: Manual Transition Schema Silent Rejection (confirm-with-schema) -# Tests: schema required alanlar eksik olunca transition 200 doner ama state degismez -############################################################################### -### Adim 1: Yeni Instance Baslat -# @name createManualSchemaRejectInstance +# ============================================================================= +# [Fact] StartTransitionSchema_AcceptsValidPayload +# orderId: ORD-VALID-001, USD 100 +# ============================================================================= + +### SV. Gecerli start +# @name startValidPayload +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "schema-valid-start-{{$timestamp}}", + "tags": ["integration-test", "schema-validation"], + "attributes": { + "orderId": "ORD-VALID-001", + "customerName": "Valid Customer", + "amount": 100.0, + "currency": "USD" + } +} + +@startValidInstanceId = {{startValidPayload.response.body.$.id}} + +### SV. State: data-initialized +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{startValidInstanceId}}/functions/state + + +# ============================================================================= +# [Fact] ConfirmTransition_WithInvalidSchemaPayload_DoesNotChangeState +# confirmed string + confirmedBy number — 4xx, state data-initialized +# ============================================================================= + +### INV.1 Baslat +# @name invalidTypeStart POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { - "key": "schema-manual-reject-{{$timestamp}}", - "tags": ["integration-test", "schema-validation", "manual-schema-reject"], + "key": "schema-inv-type-{{$timestamp}}", + "tags": ["integration-test", "schema-validation"], "attributes": { - "orderId": "ORD-MANUAL-001", - "customerName": "Manual Schema Test", + "orderId": "ORD-INV", + "customerName": "Invalid Type Test", "amount": 250, "currency": "EUR" } } -@manualRejectInstanceId = {{createManualSchemaRejectInstance.response.body.$.id}} +@invalidTypeInstanceId = {{invalidTypeStart.response.body.$.id}} -### Adim 2: Type mismatch ile dene (confirmed: "yes" string, confirmedBy: 123 number) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{manualRejectInstanceId}}/transitions/confirm-with-schema?sync=true +### INV.2 confirm — tip uyumsuzlugu (4xx beklenir) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{invalidTypeInstanceId}}/transitions/confirm-with-schema?sync=true Content-Type: application/json { @@ -262,136 +371,223 @@ Content-Type: application/json } } -### Adim 3: Durum Kontrol (HALA data-initialized olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{manualRejectInstanceId}}/functions/state +### INV.3 State: data-initialized +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{invalidTypeInstanceId}}/functions/state -############################################################################### -# TEST 8: Cancel Transition Schema (happy + invalid) -# Tests: cancel schema ile gecerli/gecersiz payload testi -############################################################################### -### Adim 1: Instance Baslat -# @name createCancelInstance +# ============================================================================= +# [Fact] ConfirmTransition_WithMissingRequiredField_DoesNotChangeState +# [Fact] ConfirmTransition_WithPartialData_DoesNotTransition ile ayni payload; +# ayri instance ile tekrarlanabilir +# ============================================================================= + +### MIS.1 Baslat +# @name missingFieldStart +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "schema-missing-cb-{{$timestamp}}", + "tags": ["integration-test", "schema-validation"], + "attributes": { + "orderId": "ORD-MIS", + "customerName": "Missing CB Test", + "amount": 1, + "currency": "USD" + } +} + +@missingCbInstanceId = {{missingFieldStart.response.body.$.id}} + +### MIS.2 confirmed true, confirmedBy yok (4xx beklenir) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{missingCbInstanceId}}/transitions/confirm-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "confirmed": true + } +} + +### MIS.3 State: data-initialized +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{missingCbInstanceId}}/functions/state + + +# ============================================================================= +# [Fact] CancelTransition_WithValidSchemaPayload_MovesToCancelled +# Once eksik cancel denemesi olmadan TEMIZ instance ile tam cancel (C# sirasinda once invalid sonra valid olabilir; burada ayri instance) +# ============================================================================= + +### CA.1 Yeni instance +# @name cancelOkStart POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { - "key": "schema-cancel-{{$timestamp}}", - "tags": ["integration-test", "schema-validation", "cancel-schema"], + "key": "schema-cancel-ok-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "cancel"], "attributes": { - "orderId": "ORD-CANCEL-001", - "customerName": "Cancel Test", + "orderId": "ORD-CANCEL-OK", + "customerName": "Cancel OK", "amount": 500, "currency": "TRY" } } -@cancelInstanceId = {{createCancelInstance.response.body.$.id}} +@cancelOkId = {{cancelOkStart.response.body.$.id}} -### Adim 2: Cancel ile EKSIK required alan (cancelReason yok) — reddedilmeli -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/transitions/cancel-with-schema?sync=true +### CA.2 cancel gecerli (C# ile ayni metinler) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelOkId}}/transitions/cancel-with-schema?sync=true Content-Type: application/json { "attributes": { + "cancelReason": "No longer needed", "cancelledBy": "test-user" } } -### Adim 3: Durum Kontrol (HALA data-initialized olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/functions/state +### CA.3 cancelled-state, C +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelOkId}}/functions/state -### Adim 4: Cancel ile GECERLI payload -# @name cancelWithValidPayload -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/transitions/cancel-with-schema?sync=true + +# ============================================================================= +# [Fact] CancelTransition_WithMissingCancelReason_DoesNotCancel +# ============================================================================= + +### CB.1 Baslat +# @name cancelBadStart +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { + "key": "schema-cancel-bad-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "cancel"], "attributes": { - "cancelReason": "No longer needed for testing", - "cancelledBy": "test-admin" + "orderId": "ORD-CANCEL-BAD", + "customerName": "Cancel Bad", + "amount": 500, + "currency": "TRY" } } -### Adim 5: Durum Kontrol (cancelled-state, status: C olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelInstanceId}}/functions/state +@cancelBadId = {{cancelBadStart.response.body.$.id}} -############################################################################### -# TEST 9: Exit Transition Schema (happy + invalid) -# Tests: exit schema ile gecerli/gecersiz payload testi -############################################################################### +### CB.2 cancelReason yok (sessiz red veya 200; state data-initialized kalmali) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelBadId}}/transitions/cancel-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "cancelledBy": "test-user" + } +} + +### CB.3 State: data-initialized +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelBadId}}/functions/state -### Adim 1: Instance Baslat -# @name createExitInstance + +# ============================================================================= +# [Fact] ExitTransition_WithValidSchemaPayload_MovesToExited +# ============================================================================= + +### EX.1 Baslat +# @name exitOkStart POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { - "key": "schema-exit-{{$timestamp}}", - "tags": ["integration-test", "schema-validation", "exit-schema"], + "key": "schema-exit-ok-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "exit"], "attributes": { - "orderId": "ORD-EXIT-001", - "customerName": "Exit Test", + "orderId": "ORD-EXIT-OK", + "customerName": "Exit OK", "amount": 750, "currency": "USD" } } -@exitInstanceId = {{createExitInstance.response.body.$.id}} +@exitOkId = {{exitOkStart.response.body.$.id}} -### Adim 2: Exit ile GECERSIZ exitCode enum (invalid-code) — reddedilmeli -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/transitions/exit-with-schema?sync=true +### EX.2 exit gecerli (C# ile uyumlu) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitOkId}}/transitions/exit-with-schema?sync=true Content-Type: application/json { "attributes": { - "exitReason": "Testing invalid code", - "exitCode": "invalid-code" + "exitReason": "Test completed", + "exitCode": "success" } } -### Adim 3: Durum Kontrol (HALA data-initialized olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/functions/state +### EX.3 exited-state, C +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitOkId}}/functions/state + -### Adim 4: Exit ile GECERLI payload -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/transitions/exit-with-schema?sync=true +# ============================================================================= +# [Fact] ExitTransition_WithInvalidExitCodeEnum_DoesNotExit +# ============================================================================= + +### EB.1 Baslat +# @name exitBadStart +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { + "key": "schema-exit-bad-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "exit"], "attributes": { - "exitReason": "Test completed successfully", - "exitCode": "success" + "orderId": "ORD-EXIT-BAD", + "customerName": "Exit Bad", + "amount": 1, + "currency": "USD" } } -### Adim 5: Durum Kontrol (exited-state, status: C olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitInstanceId}}/functions/state +@exitBadId = {{exitBadStart.response.body.$.id}} + +### EB.2 exitCode invalid-code +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitBadId}}/transitions/exit-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "exitReason": "Test", + "exitCode": "invalid-code" + } +} + +### EB.3 State: data-initialized +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{exitBadId}}/functions/state -############################################################################### -# TEST 10: SubFlow + UpdateData Schema (happy + invalid) -# Tests: SubFlow state'te updateData schema validasyonu -############################################################################### -### Adim 1: Instance Baslat -# @name createSubflowInstance +# ============================================================================= +# [Fact] UpdateData_InSubflow_WithValidSchema_UpdatesAttributes +# confirm → enter-subflow → (bekleme gerekebilir) → update-with-schema notes +# C# notes metni: "Updated via schema-validated updateData" +# Son: GET instance — eTag degisimi ve attributes.notes teyit +# ============================================================================= + +### UD.1 Baslat +# @name udStart POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { - "key": "schema-subflow-{{$timestamp}}", - "tags": ["integration-test", "schema-validation", "subflow-updatedata"], + "key": "schema-ud-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "subflow"], "attributes": { - "orderId": "ORD-SUBFLOW-001", + "orderId": "ORD-UD", "customerName": "SubFlow Test", "amount": 2000, "currency": "TRY" } } -@subflowInstanceId = {{createSubflowInstance.response.body.$.id}} +@udInstanceId = {{udStart.response.body.$.id}} -### Adim 2: confirm-with-schema ile gecis -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/confirm-with-schema?sync=true +### UD.2 confirm-with-schema +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udInstanceId}}/transitions/confirm-with-schema?sync=true Content-Type: application/json { @@ -401,37 +597,81 @@ Content-Type: application/json } } -### Adim 3: SubFlow state'e gec -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/enter-subflow?sync=true +### UD.3 enter-subflow +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udInstanceId}}/transitions/enter-subflow?sync=true Content-Type: application/json {} -### Adim 4: Durum Kontrol (subflow-state olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/functions/state +### UD.4 (istege bagli) effective state — child aktif olana kadar bekleyin +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udInstanceId}}/functions/state + +### UD.5 eTag once (istege bagli) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udInstanceId}} -### Adim 5: UpdateData ile EKSIK required alan (notes yok) — reddedilmeli -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/update-with-schema?sync=true +### UD.6 update-with-schema — C# ile ayni notes +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udInstanceId}}/transitions/update-with-schema?sync=true Content-Type: application/json { "attributes": { - "someOtherField": "not notes" + "notes": "Updated via schema-validated updateData" + } +} + +### UD.7 eTag sonra + attributes.notes +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udInstanceId}} + + +# ============================================================================= +# [Fact] UpdateData_InSubflow_WithMissingNotes_DoesNotUpdate +# someOtherField — 4xx beklenir; parent attributes'ta someOtherField OLMAMALI +# ============================================================================= + +### UD-B.1 Baslat +# @name udBadStart +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "schema-ud-bad-{{$timestamp}}", + "tags": ["integration-test", "schema-validation", "subflow"], + "attributes": { + "orderId": "ORD-UDBAD", + "customerName": "SubFlow Bad", + "amount": 2000, + "currency": "TRY" + } +} + +@udBadInstanceId = {{udBadStart.response.body.$.id}} + +### UD-B.2 confirm +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udBadInstanceId}}/transitions/confirm-with-schema?sync=true +Content-Type: application/json + +{ + "attributes": { + "confirmed": true, + "confirmedBy": "test-admin" } } -### Adim 6: Durum Kontrol (hala subflow-state olmali, eTag degismemeli) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/functions/state +### UD-B.3 enter-subflow +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udBadInstanceId}}/transitions/enter-subflow?sync=true +Content-Type: application/json -### Adim 7: UpdateData ile GECERLI payload (notes min 3 karakter) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}}/transitions/update-with-schema?sync=true +{} + +### UD-B.4 update — notes yok (4xx beklenir) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udBadInstanceId}}/transitions/update-with-schema?sync=true Content-Type: application/json { "attributes": { - "notes": "Valid update data notes" + "someOtherField": "not notes" } } -### Adim 8: Instance Body Kontrol (eTag degismis olmali) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{subflowInstanceId}} +### UD-B.5 instance attributes — someOtherField olmamali +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{udBadInstanceId}} diff --git a/api-tests/version-consistency/postman-version-consistency.json b/api-tests/version-consistency/postman-version-consistency.json index 2dd9dde..b700902 100644 --- a/api-tests/version-consistency/postman-version-consistency.json +++ b/api-tests/version-consistency/postman-version-consistency.json @@ -2,522 +2,1225 @@ "info": { "_postman_id": "f108af4c-1b76-49b2-8d26-53a2721fa7c2", "name": "vNext Test - Version Consistency", - "description": "Auto-generated from version-consistency-test.http.\nvNext workflow integration test collection for version-consistency.", + "description": "Auto-generated to mirror C# integration tests in tests/Core/Tests/version-consistency-test-workflow/. Each folder maps to one [Fact]. Authoritative version source: GET /instances/{id}.flowVersion (format: '{semver}-pkg.{pkg}+{domain}', e.g. '2.0.0-pkg.1.0.0+core').\n\nPrerequisite: both v1.0.0 and v2.0.0 published. New instances pin to latest (v2.0.0).", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "variable": [ + { "key": "baseUrl", "value": "http://localhost:4201", "type": "string" }, + { "key": "apiVersion", "value": "1", "type": "string" }, + { "key": "domain", "value": "core", "type": "string" }, + { "key": "workflow", "value": "version-consistency-test-workflow", "type": "string" } + ], + "item": [ { - "key": "baseUrl", - "value": "http://localhost:4201", - "type": "string" + "name": "S1: V2 Happy Path (full flow + data contract + version pin)", + "description": "Maps to V2HappyPath_FullFlow_DataContract_ReachesCompletedState. Asserts flowVersion semver == '2.0.0' at start and at completion, and validates data contract at every state.", + "item": [ + { + "name": "S1.1 Start instance", + "request": { + "method": "POST", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"v2-happy-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"version-consistency\"],\n \"attributes\": { \"testId\": \"v2-happy-path\" }\n}", + "options": { "raw": { "language": "json" } } + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('Status 200', function () { pm.response.to.have.status(200); });", + "pm.test('Has id', function () { pm.expect(json.id).to.be.a('string'); });", + "if (json && json.id) {", + " pm.collectionVariables.set('s1InstanceId', json.id);", + "}" + ] + } + } + ] + }, + { + "name": "S1.2 Version pin BEFORE (flowVersion semver == 2.0.0)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s1InstanceId}}"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('flowVersion exists', function () { pm.expect(json.flowVersion).to.be.a('string'); });", + "var semver = (json.flowVersion || '').split('-')[0];", + "pm.test('flowVersion semver = 2.0.0', function () { pm.expect(semver).to.eql('2.0.0'); });", + "pm.collectionVariables.set('s1FlowVersion', json.flowVersion);" + ] + } + } + ] + }, + { + "name": "S1.3 State after start (processing-state)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s1InstanceId}}", "functions", "state"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('state == processing-state', function () { pm.expect(json.state).to.eql('processing-state'); });" + ] + } + } + ] + }, + { + "name": "S1.4 Init data (initialized=true, initAt, testId)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}/functions/data", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s1InstanceId}}", "functions", "data"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('initialized == true', function () { pm.expect(json.initialized).to.eql(true); });", + "pm.test('initAt exists', function () { pm.expect(json.initAt).to.be.a('string'); });", + "pm.test('testId == v2-happy-path', function () { pm.expect(json.testId).to.eql('v2-happy-path'); });" + ] + } + } + ] + }, + { + "name": "S1.5 complete-processing → review-state", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}/transitions/complete-processing?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s1InstanceId}}", "transitions", "complete-processing"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + } + }, + { + "name": "S1.6 State == review-state", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s1InstanceId}}", "functions", "state"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('state == review-state', function () { pm.expect(json.state).to.eql('review-state'); });" + ] + } + } + ] + }, + { + "name": "S1.7 Review data (reviewExecuted=true, reviewAt)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}/functions/data", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s1InstanceId}}", "functions", "data"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('reviewExecuted == true', function () { pm.expect(json.reviewExecuted).to.eql(true); });", + "pm.test('reviewAt exists', function () { pm.expect(json.reviewAt).to.be.a('string'); });" + ] + } + } + ] + }, + { + "name": "S1.8 approve-review → completed-state", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}/transitions/approve-review?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s1InstanceId}}", "transitions", "approve-review"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + } + }, + { + "name": "S1.9 Final state == completed-state, status == C", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s1InstanceId}}", "functions", "state"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('state == completed-state', function () { pm.expect(json.state).to.eql('completed-state'); });", + "pm.test('status == C', function () { pm.expect(json.status).to.eql('C'); });" + ] + } + } + ] + }, + { + "name": "S1.10 Final data (v2Completed, completedByVersion=2.0.0, NO v1Completed)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}/functions/data", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s1InstanceId}}", "functions", "data"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('v2Completed == true', function () { pm.expect(json.v2Completed).to.eql(true); });", + "pm.test('completedByVersion == 2.0.0', function () { pm.expect(json.completedByVersion).to.eql('2.0.0'); });", + "pm.test('reviewExecuted preserved', function () { pm.expect(json.reviewExecuted).to.eql(true); });", + "pm.test('NO v1Completed flag', function () { pm.expect(json).to.not.have.property('v1Completed'); });" + ] + } + } + ] + }, + { + "name": "S1.11 Version pin AFTER (flowVersion unchanged)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s1InstanceId}}"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "var before = pm.collectionVariables.get('s1FlowVersion');", + "pm.test('flowVersion unchanged after lifecycle', function () { pm.expect(json.flowVersion).to.eql(before); });", + "var semver = (json.flowVersion || '').split('-')[0];", + "pm.test('flowVersion semver still 2.0.0', function () { pm.expect(semver).to.eql('2.0.0'); });" + ] + } + } + ] + } + ] }, { - "key": "apiVersion", - "value": "1", - "type": "string" + "name": "S2: Parallel Instances — Both reach completed independently", + "description": "Maps to ParallelInstances_BothReachCompletedIndependently.", + "item": [ + { + "name": "S2.1 Start A", + "request": { + "method": "POST", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"parallel-A-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"version-consistency\", \"parallel-A\"],\n \"attributes\": { \"testId\": \"parallel-A\" }\n}", + "options": { "raw": { "language": "json" } } + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "if (json && json.id) { pm.collectionVariables.set('s2InstanceAId', json.id); }" + ] + } + } + ] + }, + { + "name": "S2.2 Start B", + "request": { + "method": "POST", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"parallel-B-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"version-consistency\", \"parallel-B\"],\n \"attributes\": { \"testId\": \"parallel-B\" }\n}", + "options": { "raw": { "language": "json" } } + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "if (json && json.id) { pm.collectionVariables.set('s2InstanceBId', json.id); }" + ] + } + } + ] + }, + { + "name": "S2.3 A complete-processing", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s2InstanceAId}}/transitions/complete-processing?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s2InstanceAId}}", "transitions", "complete-processing"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + } + }, + { + "name": "S2.4 B complete-processing", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s2InstanceBId}}/transitions/complete-processing?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s2InstanceBId}}", "transitions", "complete-processing"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + } + }, + { + "name": "S2.5 A approve-review", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s2InstanceAId}}/transitions/approve-review?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s2InstanceAId}}", "transitions", "approve-review"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + } + }, + { + "name": "S2.6 B approve-review", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s2InstanceBId}}/transitions/approve-review?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s2InstanceBId}}", "transitions", "approve-review"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + } + }, + { + "name": "S2.7 A == completed-state", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s2InstanceAId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s2InstanceAId}}", "functions", "state"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('A on completed-state', function () { pm.expect(json.state).to.eql('completed-state'); });" + ] + } + } + ] + }, + { + "name": "S2.8 B == completed-state", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s2InstanceBId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s2InstanceBId}}", "functions", "state"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('B on completed-state', function () { pm.expect(json.state).to.eql('completed-state'); });" + ] + } + } + ] + } + ] }, { - "key": "domain", - "value": "core", - "type": "string" + "name": "S3: Parallel Instances — Different speeds", + "description": "Maps to ParallelInstances_DifferentSpeeds_AWaitsWhileBCompletes. A stays on processing while B finishes the full path; then A completes.", + "item": [ + { + "name": "S3.1 Start A", + "request": { + "method": "POST", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"speed-A-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"version-consistency\", \"speed-A\"],\n \"attributes\": { \"testId\": \"speed-A\" }\n}", + "options": { "raw": { "language": "json" } } + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "if (json && json.id) { pm.collectionVariables.set('s3InstanceAId', json.id); }" + ] + } + } + ] + }, + { + "name": "S3.2 Start B", + "request": { + "method": "POST", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"speed-B-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"version-consistency\", \"speed-B\"],\n \"attributes\": { \"testId\": \"speed-B\" }\n}", + "options": { "raw": { "language": "json" } } + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "if (json && json.id) { pm.collectionVariables.set('s3InstanceBId', json.id); }" + ] + } + } + ] + }, + { + "name": "S3.3 B complete-processing", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s3InstanceBId}}/transitions/complete-processing?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s3InstanceBId}}", "transitions", "complete-processing"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + } + }, + { + "name": "S3.4 B approve-review", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s3InstanceBId}}/transitions/approve-review?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s3InstanceBId}}", "transitions", "approve-review"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + } + }, + { + "name": "S3.5 B == completed-state", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s3InstanceBId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s3InstanceBId}}", "functions", "state"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('B on completed-state', function () { pm.expect(json.state).to.eql('completed-state'); });" + ] + } + } + ] + }, + { + "name": "S3.6 A still on processing-state", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s3InstanceAId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s3InstanceAId}}", "functions", "state"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('A still on processing-state', function () { pm.expect(json.state).to.eql('processing-state'); });" + ] + } + } + ] + }, + { + "name": "S3.7 A complete-processing", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s3InstanceAId}}/transitions/complete-processing?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s3InstanceAId}}", "transitions", "complete-processing"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + } + }, + { + "name": "S3.8 A approve-review", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s3InstanceAId}}/transitions/approve-review?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s3InstanceAId}}", "transitions", "approve-review"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + } + }, + { + "name": "S3.9 A == completed-state", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s3InstanceAId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s3InstanceAId}}", "functions", "state"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('A on completed-state', function () { pm.expect(json.state).to.eql('completed-state'); });" + ] + } + } + ] + } + ] }, { - "key": "workflow", - "value": "version-consistency-test-workflow", - "type": "string" - } - ], - "item": [ - { - "name": "ADIM 2: Instance A baslatma (v1.0.0 uzerinde)", + "name": "S4: Idempotent Start + Version Pinning (CRITICAL)", + "description": "Maps to IdempotentStart_V1Instance_RemainsOnV1Path_NotUpgradedToV2.\n\nCRITICAL: Re-posting start with the SAME key MUST return the SAME instance id, and flowVersion MUST remain unchanged. (E.g. an instance started on v1.0.0 must remain on v1.0.0 after a second start call, never auto-upgraded.)", "item": [ { - "name": "Instance A - Baslat (v1.0.0 akisinda calisacak)", + "name": "S4.0 Pre-generate idempotent key", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/health", + "host": ["{{baseUrl}}"], + "path": ["health"] + }, + "description": "Set s4Key collection variable in the test script for use in subsequent requests." + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var key = 'version-pin-idempotent-' + Date.now();", + "pm.collectionVariables.set('s4Key', key);" + ] + } + } + ] + }, + { + "name": "S4.1 First start (creates instance)", + "request": { + "method": "POST", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"{{s4Key}}\",\n \"tags\": [\"integration-test\", \"version-consistency\", \"idempotent\"],\n \"attributes\": { \"testId\": \"idempotent-version-pin\" }\n}", + "options": { "raw": { "language": "json" } } + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "if (json && json.id) { pm.collectionVariables.set('s4InstanceId', json.id); }" + ] + } + } + ] + }, + { + "name": "S4.2 State == processing-state", "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], + "method": "GET", + "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "start" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] - }, - "body": { - "mode": "raw", - "raw": "{\n \"key\": \"version-test-A-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"version-consistency\", \"v1-instance\"],\n \"attributes\": {\n \"testId\": \"version-consistency-A\"\n }\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s4InstanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s4InstanceId}}", "functions", "state"] } }, - "response": [], "event": [ { "listen": "test", "script": { "type": "text/javascript", "exec": [ - "var jsonData = pm.response.json();", - "if (jsonData && jsonData.id) {", - " pm.collectionVariables.set(\"instanceAId\", jsonData.id);", - " console.log(\"instanceAId set to: \" + jsonData.id);", - "}" + "var json = pm.response.json();", + "pm.test('state == processing-state', function () { pm.expect(json.state).to.eql('processing-state'); });" ] } } ] - } - ] - }, - { - "name": "ADIM 3: Instance A state kontrolu (processing-state'te olmali)", - "item": [ + }, { - "name": "Instance A - State kontrolu (processing-state bekleniyor)", + "name": "S4.3 flowVersion BEFORE second start", "request": { "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceAId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceAId}}", - "functions", - "state" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "ADIM 5: Instance B baslatma (v2.0.0 uzerinde calisacak)", - "item": [ + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s4InstanceId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s4InstanceId}}"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('flowVersion exists', function () { pm.expect(json.flowVersion).to.be.a('string'); });", + "pm.collectionVariables.set('s4FlowVersionBefore', json.flowVersion);" + ] + } + } + ] + }, { - "name": "Instance B - Baslat (v2.0.0 akisinda calisacak)", + "name": "S4.4 Second start with SAME key (idempotent)", "request": { "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "start" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], + "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", - "raw": "{\n \"key\": \"version-test-B-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"version-consistency\", \"v2-instance\"],\n \"attributes\": {\n \"testId\": \"version-consistency-B\"\n }\n}", - "options": { - "raw": { - "language": "json" - } + "raw": "{\n \"key\": \"{{s4Key}}\",\n \"tags\": [\"integration-test\", \"version-consistency\", \"idempotent\"],\n \"attributes\": { \"testId\": \"idempotent-version-pin\" }\n}", + "options": { "raw": { "language": "json" } } + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "var first = pm.collectionVariables.get('s4InstanceId');", + "pm.test('Same instance id returned', function () { pm.expect(json.id).to.eql(first); });" + ] } } + ] + }, + { + "name": "S4.5 flowVersion AFTER second start (must equal BEFORE)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s4InstanceId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s4InstanceId}}"] + } }, - "response": [], "event": [ { "listen": "test", "script": { "type": "text/javascript", "exec": [ - "var jsonData = pm.response.json();", - "if (jsonData && jsonData.id) {", - " pm.collectionVariables.set(\"instanceBId\", jsonData.id);", - " console.log(\"instanceBId set to: \" + jsonData.id);", - "}" + "var json = pm.response.json();", + "var before = pm.collectionVariables.get('s4FlowVersionBefore');", + "pm.test('flowVersion unchanged after re-start', function () { pm.expect(json.flowVersion).to.eql(before); });" ] } } ] - } - ] - }, - { - "name": "ADIM 6: Instance B state kontrolu (processing-state'te olmali)", - "item": [ + }, + { + "name": "S4.6 State still processing-state", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s4InstanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s4InstanceId}}", "functions", "state"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('state == processing-state (not re-initialized)', function () { pm.expect(json.state).to.eql('processing-state'); });" + ] + } + } + ] + }, { - "name": "Instance B - State kontrolu (processing-state bekleniyor)", + "name": "S4.7 Data unchanged (initialized=true, testId preserved)", "request": { "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceBId}}", - "functions", - "state" - ] - } - }, - "response": [] + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s4InstanceId}}/functions/data", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s4InstanceId}}", "functions", "data"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('initialized preserved', function () { pm.expect(json.initialized).to.eql(true); });", + "pm.test('testId preserved', function () { pm.expect(json.testId).to.eql('idempotent-version-pin'); });" + ] + } + } + ] } ] }, { - "name": "ADIM 7: Instance A'yi tamamla", + "name": "S5: Path Selection Driven by Actual flowVersion", + "description": "Maps to PathSelection_DrivenByActualFlowVersion_ReachesVersionAppropriateCompletedState. Read flowVersion first, then drive the path that matches the version.", "item": [ { - "name": "Instance A - complete-processing (v1: dogrudan completed-state'e gitmeli)", + "name": "S5.1 Start instance", "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceAId}}/transitions/complete-processing?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceAId}}", - "transitions", - "complete-processing" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] + "method": "POST", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], + "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", - "raw": "{}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n \"key\": \"path-selection-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"version-consistency\", \"path-selection\"],\n \"attributes\": { \"testId\": \"path-selection\" }\n}", + "options": { "raw": { "language": "json" } } } }, - "response": [] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "if (json && json.id) { pm.collectionVariables.set('s5InstanceId', json.id); }" + ] + } + } + ] }, { - "name": "Instance A - Final state kontrolu (completed-state bekleniyor)", + "name": "S5.2 Read flowVersion (drives subsequent path)", "request": { "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceAId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceAId}}", - "functions", - "state" - ] + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s5InstanceId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s5InstanceId}}"] } }, - "response": [] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "var semver = (json.flowVersion || '').split('-')[0];", + "pm.test('flowVersion semver in {1.0.0, 2.0.0}', function () { pm.expect(['1.0.0', '2.0.0']).to.include(semver); });", + "pm.collectionVariables.set('s5Semver', semver);" + ] + } + } + ] + }, + { + "name": "S5.3 complete-processing", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s5InstanceId}}/transitions/complete-processing?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s5InstanceId}}", "transitions", "complete-processing"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + } }, { - "name": "Instance A - Data kontrolu", + "name": "S5.4 State after complete-processing (depends on version)", "request": { "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceAId}}/functions/data", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceAId}}", - "functions", - "data" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "ADIM 8: Instance B'yi processing'den ilerlet", - "item": [ + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s5InstanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s5InstanceId}}", "functions", "state"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "var semver = pm.collectionVariables.get('s5Semver');", + "if (semver === '2.0.0') {", + " pm.test('v2 → review-state', function () { pm.expect(json.state).to.eql('review-state'); });", + "} else {", + " pm.test('v1 → completed-state (skips review)', function () { pm.expect(json.state).to.eql('completed-state'); });", + "}" + ] + } + } + ] + }, { - "name": "Instance B - complete-processing (v2: review-state'e gitmeli)", + "name": "S5.5 approve-review (run only if v2 — for v1 this is a no-op/error)", "request": { "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/transitions/complete-processing?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceBId}}", - "transitions", - "complete-processing" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s5InstanceId}}/transitions/approve-review?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s5InstanceId}}", "transitions", "approve-review"], + "query": [{ "key": "sync", "value": "true" }] }, - "body": { - "mode": "raw", - "raw": "{}", - "options": { - "raw": { - "language": "json" - } + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "var semver = pm.collectionVariables.get('s5Semver');", + "if (semver !== '2.0.0') {", + " console.log('Skipping approve-review for v1 instance');", + " postman.setNextRequest('S5.6 Final state == completed-state');", + "}" + ] } } + ] + }, + { + "name": "S5.6 Final state == completed-state", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s5InstanceId}}/functions/state", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s5InstanceId}}", "functions", "state"] + } }, - "response": [] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('final state == completed-state', function () { pm.expect(json.state).to.eql('completed-state'); });", + "pm.test('status == C', function () { pm.expect(json.status).to.eql('C'); });" + ] + } + } + ] }, { - "name": "Instance B - State kontrolu (review-state bekleniyor)", + "name": "S5.7 Final data (version-appropriate flags)", "request": { "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceBId}}", - "functions", - "state" - ] + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s5InstanceId}}/functions/data", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s5InstanceId}}", "functions", "data"] } }, - "response": [] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "var semver = pm.collectionVariables.get('s5Semver');", + "pm.test('completedByVersion matches semver', function () { pm.expect(json.completedByVersion).to.eql(semver); });", + "if (semver === '2.0.0') {", + " pm.test('v2: v2Completed=true', function () { pm.expect(json.v2Completed).to.eql(true); });", + " pm.test('v2: reviewExecuted=true', function () { pm.expect(json.reviewExecuted).to.eql(true); });", + " pm.test('v2: NO v1Completed', function () { pm.expect(json).to.not.have.property('v1Completed'); });", + "} else {", + " pm.test('v1: v1Completed=true', function () { pm.expect(json.v1Completed).to.eql(true); });", + " pm.test('v1: NO reviewExecuted', function () { pm.expect(json).to.not.have.property('reviewExecuted'); });", + " pm.test('v1: NO v2Completed', function () { pm.expect(json).to.not.have.property('v2Completed'); });", + "}" + ] + } + } + ] }, { - "name": "Instance B - Data kontrolu (reviewExecuted=true olmali)", + "name": "S5.8 Version pin unchanged after completion", "request": { "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/functions/data", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceBId}}", - "functions", - "data" - ] - } - }, - "response": [] + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s5InstanceId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s5InstanceId}}"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "var semver = (json.flowVersion || '').split('-')[0];", + "var initial = pm.collectionVariables.get('s5Semver');", + "pm.test('flowVersion semver unchanged', function () { pm.expect(semver).to.eql(initial); });" + ] + } + } + ] } ] }, { - "name": "ADIM 9: Instance B review'dan completed'a", + "name": "S6: flowVersion Stability Across Lifecycle", + "description": "Maps to FlowVersion_StableAcrossLifecycle_NeverChanges. flowVersion captured at processing-state must equal flowVersion at review-state and at completed-state.", "item": [ { - "name": "Instance B - approve-review (v2: completed-state'e gitmeli)", + "name": "S6.1 Start instance", "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/transitions/approve-review?sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceBId}}", - "transitions", - "approve-review" - ], - "query": [ - { - "key": "sync", - "value": "true" - } - ] + "method": "POST", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "start"], + "query": [{ "key": "sync", "value": "true" }] }, "body": { "mode": "raw", - "raw": "{}", - "options": { - "raw": { - "language": "json" - } + "raw": "{\n \"key\": \"version-stability-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"version-consistency\", \"stability\"],\n \"attributes\": { \"testId\": \"version-stability\" }\n}", + "options": { "raw": { "language": "json" } } + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "if (json && json.id) { pm.collectionVariables.set('s6InstanceId', json.id); }" + ] } } + ] + }, + { + "name": "S6.2 flowVersion at processing-state (capture)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s6InstanceId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s6InstanceId}}"] + } }, - "response": [] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "pm.test('flowVersion captured', function () { pm.expect(json.flowVersion).to.be.a('string'); });", + "pm.collectionVariables.set('s6FlowVersion', json.flowVersion);" + ] + } + } + ] + }, + { + "name": "S6.3 complete-processing", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s6InstanceId}}/transitions/complete-processing?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s6InstanceId}}", "transitions", "complete-processing"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + } }, { - "name": "Instance B - Final state kontrolu (completed-state bekleniyor)", + "name": "S6.4 flowVersion at review-state (must equal S6.2)", "request": { "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceBId}}", - "functions", - "state" - ] + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s6InstanceId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s6InstanceId}}"] } }, - "response": [] + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "var initial = pm.collectionVariables.get('s6FlowVersion');", + "pm.test('flowVersion stable at review-state', function () { pm.expect(json.flowVersion).to.eql(initial); });" + ] + } + } + ] + }, + { + "name": "S6.5 approve-review", + "request": { + "method": "PATCH", + "header": [{ "key": "Content-Type", "value": "application/json", "type": "text" }], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s6InstanceId}}/transitions/approve-review?sync=true", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s6InstanceId}}", "transitions", "approve-review"], + "query": [{ "key": "sync", "value": "true" }] + }, + "body": { "mode": "raw", "raw": "{}", "options": { "raw": { "language": "json" } } } + } }, { - "name": "Instance B - Final data kontrolu", + "name": "S6.6 flowVersion at completed-state (must equal S6.2)", "request": { "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/functions/data", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "api", - "v{{apiVersion}}", - "{{domain}}", - "workflows", - "{{workflow}}", - "instances", - "{{instanceBId}}", - "functions", - "data" - ] - } - }, - "response": [] + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s6InstanceId}}", + "host": ["{{baseUrl}}"], + "path": ["api", "v{{apiVersion}}", "{{domain}}", "workflows", "{{workflow}}", "instances", "{{s6InstanceId}}"] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var json = pm.response.json();", + "var initial = pm.collectionVariables.get('s6FlowVersion');", + "pm.test('flowVersion stable at completed-state', function () { pm.expect(json.flowVersion).to.eql(initial); });" + ] + } + } + ] } ] } ] -} \ No newline at end of file +} diff --git a/api-tests/version-consistency/version-consistency-test.http b/api-tests/version-consistency/version-consistency-test.http index 65822f5..ce950dc 100644 --- a/api-tests/version-consistency/version-consistency-test.http +++ b/api-tests/version-consistency/version-consistency-test.http @@ -6,154 +6,380 @@ ############################################################################### # VERSION CONSISTENCY TEST # -# Validates that when the workflow version changes, existing instances keep -# following the flow pinned to their own definition version. +# Aligned with C# integration tests in: +# tests/Core/Tests/version-consistency-test-workflow/ +# - VersionConsistencyTestWorkflowTests.cs +# - VersionConsistencyScenarioActions.cs +# - VersionConsistencyInstanceDataAssertions.cs # -# PREREQUISITE: Only v1.0.0 should be published. -# v2.0.0 must NOT yet be published. +# AUTHORITATIVE VERSION SOURCE: +# GET /instances/{id} response body has a "flowVersion" field with format: +# "2.0.0-pkg.1.0.0+core" (workflow semver - pkg. + ) +# Take the part BEFORE the first "-" to get the workflow semver ("2.0.0"). # -# STEPS: -# 1. Publish v1.0.0 workflow (version-consistency-test-workflow.json) -# 2. Start instance A (runs on v1.0.0) -# 3. Move instance A to processing-state (auto) -# 4. Publish v2.0.0 workflow (version-consistency-test-workflow-v2.json) -# 5. Start instance B (runs on v2.0.0) -# 6. Leave instance B on processing-state -# 7. Instance A: complete-processing → completed-state (v1 path, NO review) -# 8. Instance B: complete-processing → review-state (v2 path) -# 9. Instance B: approve-review → completed-state -# 10. Compare data on both instances +# PREREQUISITE: +# Both v1.0.0 and v2.0.0 are published. +# Runtime always pins NEW instances to the LATEST version (v2.0.0). +# +# Each scenario below maps to one [Fact] in the C# test class. ############################################################################### -############################################################################### -# STEP 1: v1.0.0 MUST BE PUBLISHED -# → Publish version-consistency-test-workflow.json -############################################################################### ############################################################################### -# STEP 2: Start instance A (v1.0.0 line) +# SCENARIO 1: V2 Happy Path — full flow + every-state data contract + version pin +# Maps to: V2HappyPath_FullFlow_DataContract_ReachesCompletedState +# Path: init → (auto) → processing → review → completed +# Assertions: +# - state name + status "C" at completion +# - flowVersion semver = "2.0.0" at start AND at completion +# - InitVersionMapping: initialized=true, initAt, testId +# - ReviewMapping: reviewExecuted=true, reviewAt +# - V2CompletedMapping: v2Completed=true, completedByVersion="2.0.0" +# - Negative: v1Completed must be absent ############################################################################### -### Instance A - Start (pinned to v1.0.0) -# @name createInstanceA +### S1.1 Start instance +# @name s1Start POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { - "key": "version-test-A-{{$timestamp}}", - "tags": ["integration-test", "version-consistency", "v1-instance"], - "attributes": { - "testId": "version-consistency-A" - } + "key": "v2-happy-{{$timestamp}}", + "tags": ["integration-test", "version-consistency"], + "attributes": { "testId": "v2-happy-path" } } -@instanceAId = {{createInstanceA.response.body.$.id}} +@s1InstanceId = {{s1Start.response.body.$.id}} -############################################################################### -# STEP 3: Instance A state check (should be processing-state) -############################################################################### +### S1.2 Version pin check (BEFORE) — flowVersion in GET /instances/{id} +# Expected flowVersion: "2.0.0-pkg.+core" → semver "2.0.0" +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}} -### Instance A - State check (expect processing-state) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceAId}}/functions/state +### S1.3 State after start (auto transition → processing-state) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}/functions/state + +### S1.4 Init mapping data check (initialized=true, initAt, testId) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}/functions/data + +### S1.5 Advance to review-state (v2 path) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}/transitions/complete-processing?sync=true +Content-Type: application/json + +{} + +### S1.6 Review state check + ReviewMapping data (reviewExecuted=true) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}/functions/state + +### S1.7 Approve review → completed-state +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}/transitions/approve-review?sync=true +Content-Type: application/json + +{} + +### S1.8 Final state check (completed-state, status "C") +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}/functions/state + +### S1.9 Final data check (v2Completed=true, completedByVersion="2.0.0", NO v1Completed) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}}/functions/data + +### S1.10 Version pin check (AFTER) — flowVersion semver still "2.0.0" +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s1InstanceId}} -############################################################################### -# STEP 4: Publish v2.0.0 -# → Publish version-consistency-test-workflow-v2.json -# Instance A remains on processing-state. -############################################################################### ############################################################################### -# STEP 5: Start instance B (v2.0.0 line) +# SCENARIO 2: Parallel Instances — both reach completed independently +# Maps to: ParallelInstances_BothReachCompletedIndependently ############################################################################### -### Instance B - Start (pinned to v2.0.0) -# @name createInstanceB +### S2.1 Start instance A +# @name s2StartA +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "parallel-A-{{$timestamp}}", + "tags": ["integration-test", "version-consistency", "parallel-A"], + "attributes": { "testId": "parallel-A" } +} + +@s2InstanceAId = {{s2StartA.response.body.$.id}} + +### S2.2 Start instance B +# @name s2StartB POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { - "key": "version-test-B-{{$timestamp}}", - "tags": ["integration-test", "version-consistency", "v2-instance"], - "attributes": { - "testId": "version-consistency-B" - } + "key": "parallel-B-{{$timestamp}}", + "tags": ["integration-test", "version-consistency", "parallel-B"], + "attributes": { "testId": "parallel-B" } } -@instanceBId = {{createInstanceB.response.body.$.id}} +@s2InstanceBId = {{s2StartB.response.body.$.id}} + +### S2.3 Advance both to review +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s2InstanceAId}}/transitions/complete-processing?sync=true +Content-Type: application/json + +{} + +### +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s2InstanceBId}}/transitions/complete-processing?sync=true +Content-Type: application/json + +{} + +### S2.4 Approve both +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s2InstanceAId}}/transitions/approve-review?sync=true +Content-Type: application/json + +{} + +### +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s2InstanceBId}}/transitions/approve-review?sync=true +Content-Type: application/json + +{} + +### S2.5 Both must be on completed-state +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s2InstanceAId}}/functions/state + +### +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s2InstanceBId}}/functions/state + ############################################################################### -# STEP 6: Instance B state check (should be processing-state) +# SCENARIO 3: Parallel Instances — Different speeds (A waits while B completes) +# Maps to: ParallelInstances_DifferentSpeeds_AWaitsWhileBCompletes ############################################################################### -### Instance B - State check (expect processing-state) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/functions/state +### S3.1 Start A +# @name s3StartA +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "speed-A-{{$timestamp}}", + "tags": ["integration-test", "version-consistency", "speed-A"], + "attributes": { "testId": "speed-A" } +} + +@s3InstanceAId = {{s3StartA.response.body.$.id}} + +### S3.2 Start B +# @name s3StartB +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "speed-B-{{$timestamp}}", + "tags": ["integration-test", "version-consistency", "speed-B"], + "attributes": { "testId": "speed-B" } +} + +@s3InstanceBId = {{s3StartB.response.body.$.id}} + +### S3.3 B completes full path +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s3InstanceBId}}/transitions/complete-processing?sync=true +Content-Type: application/json + +{} + +### +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s3InstanceBId}}/transitions/approve-review?sync=true +Content-Type: application/json + +{} + +### S3.4 B should be completed +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s3InstanceBId}}/functions/state + +### S3.5 A should still be on processing-state +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s3InstanceAId}}/functions/state + +### S3.6 Now advance A +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s3InstanceAId}}/transitions/complete-processing?sync=true +Content-Type: application/json + +{} + +### +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s3InstanceAId}}/transitions/approve-review?sync=true +Content-Type: application/json + +{} + +### S3.7 A must be on completed-state +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s3InstanceAId}}/functions/state + ############################################################################### -# STEP 7: Complete instance A -# complete-processing → completed-state (v1 path, NO review-state) +# SCENARIO 4: Idempotent Start + Version Pinning (CRITICAL) +# Maps to: IdempotentStart_V1Instance_RemainsOnV1Path_NotUpgradedToV2 +# +# Goal: Prove that posting "start" twice with the SAME key: +# 1. Returns the SAME instance id (idempotent) +# 2. Does NOT upgrade the instance to a different version +# 3. Instance state and data remain unchanged +# +# Authoritative version check via GET /instances/{id} → flowVersion semver. ############################################################################### -### Instance A - complete-processing (v1: should land on completed-state) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceAId}}/transitions/complete-processing?sync=true +@s4Key = version-pin-idempotent-{{$timestamp}} + +### S4.1 First start (creates instance) +# @name s4First +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json -{} +{ + "key": "{{s4Key}}", + "tags": ["integration-test", "version-consistency", "idempotent"], + "attributes": { "testId": "idempotent-version-pin" } +} + +@s4InstanceId = {{s4First.response.body.$.id}} + +### S4.2 Confirm processing-state +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s4InstanceId}}/functions/state + +### S4.3 Read flowVersion BEFORE second start +# @name s4Before +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s4InstanceId}} + +### S4.4 Second start with SAME key — must return the same id +# @name s4Second +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "{{s4Key}}", + "tags": ["integration-test", "version-consistency", "idempotent"], + "attributes": { "testId": "idempotent-version-pin" } +} + +@s4SecondId = {{s4Second.response.body.$.id}} + +### S4.5 Verify SAME id returned (s4SecondId == s4InstanceId) +# Manual check: compare s4SecondId and s4InstanceId in test client output. + +### S4.6 Read flowVersion AFTER second start (must equal s4Before's flowVersion) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s4SecondId}} -### Instance A - Final state (expect completed-state) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceAId}}/functions/state +### S4.7 Confirm state still processing-state (not re-initialized) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s4SecondId}}/functions/state + +### S4.8 Confirm data unchanged (initialized=true, testId preserved) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s4SecondId}}/functions/data -### Instance A - Data check -# EXPECTED: v1Completed=true; reviewExecuted must NOT appear -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceAId}}/functions/data ############################################################################### -# STEP 8: Advance instance B from processing -# complete-processing → review-state (v2 extra state) +# SCENARIO 5: Path Selection Driven by Actual flowVersion +# Maps to: PathSelection_DrivenByActualFlowVersion_ReachesVersionAppropriateCompletedState +# +# Read flowVersion first, then drive the path that matches the version. +# In current test setup, expected version is "2.0.0" (latest published). ############################################################################### -### Instance B - complete-processing (v2: expect review-state) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/transitions/complete-processing?sync=true +### S5.1 Start instance +# @name s5Start +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "path-selection-{{$timestamp}}", + "tags": ["integration-test", "version-consistency", "path-selection"], + "attributes": { "testId": "path-selection" } +} + +@s5InstanceId = {{s5Start.response.body.$.id}} + +### S5.2 Read flowVersion to determine which path the instance is on +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s5InstanceId}} + +### S5.3 complete-processing transition (target depends on version) +# v2.0.0 → review-state +# v1.0.0 → completed-state (review-state does NOT exist in v1) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s5InstanceId}}/transitions/complete-processing?sync=true +Content-Type: application/json + +{} + +### S5.4 Check actual state after transition +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s5InstanceId}}/functions/state + +### S5.5 If on review-state (v2 path) → approve-review +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s5InstanceId}}/transitions/approve-review?sync=true Content-Type: application/json {} -### Instance B - State check (expect review-state) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/functions/state +### S5.6 Final state — must be completed-state +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s5InstanceId}}/functions/state + +### S5.7 Final data — version-appropriate flags +# v2.0.0: v2Completed=true, completedByVersion="2.0.0", reviewExecuted=true, NO v1Completed +# v1.0.0: v1Completed=true, completedByVersion="1.0.0", NO reviewExecuted +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s5InstanceId}}/functions/data + +### S5.8 Version pin unchanged after completion +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s5InstanceId}} -### Instance B - Data check (reviewExecuted=true) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/functions/data ############################################################################### -# STEP 9: Instance B review → completed +# SCENARIO 6: flowVersion Stability Across Lifecycle +# Maps to: FlowVersion_StableAcrossLifecycle_NeverChanges +# +# Verify flowVersion is identical at every step (start → review → completed). ############################################################################### -### Instance B - approve-review (v2: completed-state) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/transitions/approve-review?sync=true +### S6.1 Start instance +# @name s6Start +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "version-stability-{{$timestamp}}", + "tags": ["integration-test", "version-consistency", "stability"], + "attributes": { "testId": "version-stability" } +} + +@s6InstanceId = {{s6Start.response.body.$.id}} + +### S6.2 flowVersion at processing-state +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s6InstanceId}} + +### S6.3 Move to review (v2 path) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s6InstanceId}}/transitions/complete-processing?sync=true +Content-Type: application/json + +{} + +### S6.4 flowVersion at review-state (must equal S6.2) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s6InstanceId}} + +### S6.5 Approve review +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s6InstanceId}}/transitions/approve-review?sync=true Content-Type: application/json {} -### Instance B - Final state (expect completed-state) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/functions/state +### S6.6 flowVersion at completed-state (must equal S6.2 and S6.4) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{s6InstanceId}} -### Instance B - Final data -# EXPECTED: reviewExecuted=true, v2Completed=true; v1Completed must NOT be set -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{instanceBId}}/functions/data ############################################################################### # VALIDATION SUMMARY # -# Instance A (v1.0.0): -# - State: completed-state -# - Data: v1Completed=true, completedByVersion="1.0.0" -# - reviewExecuted must be absent (no review-state on v1) -# -# Instance B (v2.0.0): -# - State: completed-state -# - Data: reviewExecuted=true, v2Completed=true, completedByVersion="2.0.0" -# - v1Completed must NOT be set (followed v2 path) +# S1: V2 happy path — every-state data + flowVersion pin +# S2: Parallel instances — independent completion +# S3: Different speeds — A waits, B completes +# S4: Idempotent start — same id, version unchanged +# S5: Path selection — drive workflow based on actual flowVersion +# S6: Version stability — flowVersion never changes during lifecycle # -# SUCCESS: -# After publishing v2, instance A still completes on the v1 path and reaches -# completed without passing through review-state. +# AUTHORITATIVE VERSION SOURCE: +# GET /instances/{id} → response.flowVersion +# Format: "{semver}-pkg.{packageVersion}+{domain}" +# Example: "2.0.0-pkg.1.0.0+core" → workflow semver = "2.0.0" ############################################################################### diff --git a/core.link.json b/core.link.json index 49cf9ce..f47204b 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-05-07T11:59:04.804Z" + "importedAt": "2026-05-08T06:33:38.552Z" } \ No newline at end of file diff --git a/core/Workflows/error-boundary/.meta/error-boundary-test-workflow.diagram.json b/core/Workflows/error-boundary/.meta/error-boundary-test-workflow.diagram.json new file mode 100644 index 0000000..00a4467 --- /dev/null +++ b/core/Workflows/error-boundary/.meta/error-boundary-test-workflow.diagram.json @@ -0,0 +1,3 @@ +{ + "nodePos": {} +} \ No newline at end of file diff --git a/core/Workflows/version-consistency/.meta/version-consistency-test-workflow-v2.diagram.json b/core/Workflows/version-consistency/.meta/version-consistency-test-workflow-v2.diagram.json new file mode 100644 index 0000000..00a4467 --- /dev/null +++ b/core/Workflows/version-consistency/.meta/version-consistency-test-workflow-v2.diagram.json @@ -0,0 +1,3 @@ +{ + "nodePos": {} +} \ No newline at end of file diff --git a/core/Workflows/version-consistency/.meta/version-consistency-test-workflow.diagram.json b/core/Workflows/version-consistency/.meta/version-consistency-test-workflow.diagram.json new file mode 100644 index 0000000..00a4467 --- /dev/null +++ b/core/Workflows/version-consistency/.meta/version-consistency-test-workflow.diagram.json @@ -0,0 +1,3 @@ +{ + "nodePos": {} +} \ No newline at end of file diff --git a/tests/Core/Tests/version-consistency-test-workflow/VersionConsistencyInstanceDataAssertions.cs b/tests/Core/Tests/version-consistency-test-workflow/VersionConsistencyInstanceDataAssertions.cs new file mode 100644 index 0000000..42171cd --- /dev/null +++ b/tests/Core/Tests/version-consistency-test-workflow/VersionConsistencyInstanceDataAssertions.cs @@ -0,0 +1,191 @@ +using System.Text.Json; +using Core.IntegrationTests.Helpers; +using Xunit; + +namespace Core.IntegrationTests.Tests.VersionConsistencyTestWorkflow; + +/// +/// Instance attributes contract assertions for version-consistency-test-workflow. +/// Field names align with core/Workflows/version-consistency/src/*.csx. +/// +public static class VersionConsistencyInstanceDataAssertions +{ + private const string ContractHint = + "(version-consistency-test-workflow *.csx mapping contract)"; + + /// + /// Reads flowVersion from a GET /instances/{id} response body. + /// This is the authoritative source for the version an instance is pinned to. + /// See vnext-runtime/doc/tr/how-to/start-instance.md (~line 219) for the response shape. + /// + /// Format observed in this runtime: "{workflowVersion}-pkg.{packageVersion}+{domain}", + /// e.g. "2.0.0-pkg.1.0.0+core". The leading semver segment is the workflow version. + /// + public static string ExtractFlowVersion(JsonElement instanceBody) + { + Assert.True( + instanceBody.TryGetProperty("flowVersion", out var versionEl) + && versionEl.ValueKind == JsonValueKind.String, + "GET /instances/{id} response should include 'flowVersion' as a string." + ); + var version = versionEl.GetString(); + Assert.False( + string.IsNullOrWhiteSpace(version), + "'flowVersion' should be a non-empty string." + ); + return version!; + } + + /// + /// Extracts the workflow semver portion (e.g. "2.0.0") from a full + /// flowVersion string like "2.0.0-pkg.1.0.0+core". + /// + public static string ExtractWorkflowSemver(JsonElement instanceBody) + { + var full = ExtractFlowVersion(instanceBody); + var dashIdx = full.IndexOf('-'); + return dashIdx > 0 ? full.Substring(0, dashIdx) : full; + } + + /// + /// Asserts that the instance's workflow semver portion equals . + /// E.g. AssertWorkflowVersion(body, "2.0.0") matches "2.0.0-pkg.1.0.0+core". + /// + public static void AssertWorkflowVersion(JsonElement instanceBody, string expectedSemver) + { + var actual = ExtractWorkflowSemver(instanceBody); + var full = ExtractFlowVersion(instanceBody); + Assert.True( + actual == expectedSemver, + $"Expected workflow semver '{expectedSemver}', got '{actual}' (full flowVersion='{full}')." + ); + } + + /// + /// After start + auto transition to processing-state: + /// initialized=true, initAt (ISO-8601), optionally testId. + /// + public static void AssertInitializedAttributes(JsonElement attrs, string? expectedTestId = null) + { + JsonElementAssertions.AssertPropertyTrue( + attrs, + "initialized", + $"InitVersionMapping should set initialized=true {ContractHint}" + ); + JsonElementAssertions.AssertPropertyNonEmptyString( + attrs, + "initAt", + $"InitVersionMapping should set initAt as ISO-8601 {ContractHint}" + ); + + if (expectedTestId is not null) + { + JsonElementAssertions.AssertPropertyString( + attrs, + "testId", + expectedTestId, + $"InitVersionMapping should preserve testId from start attributes {ContractHint}" + ); + } + } + + /// + /// After review-state onEntries (v2 only): reviewExecuted=true, reviewAt non-empty. + /// + public static void AssertReviewExecuted(JsonElement attrs) + { + JsonElementAssertions.AssertPropertyTrue( + attrs, + "reviewExecuted", + $"ReviewMapping should set reviewExecuted=true {ContractHint}" + ); + JsonElementAssertions.AssertPropertyNonEmptyString( + attrs, + "reviewAt", + $"ReviewMapping should set reviewAt as ISO-8601 {ContractHint}" + ); + } + + /// + /// V2 completed-state onEntries: v2Completed=true, completedByVersion="2.0.0", + /// completedAt non-empty. + /// + public static void AssertV2Completed(JsonElement attrs) + { + JsonElementAssertions.AssertPropertyTrue( + attrs, + "v2Completed", + $"V2CompletedMapping should set v2Completed=true {ContractHint}" + ); + JsonElementAssertions.AssertPropertyString( + attrs, + "completedByVersion", + "2.0.0", + $"V2CompletedMapping should set completedByVersion='2.0.0' {ContractHint}" + ); + JsonElementAssertions.AssertPropertyNonEmptyString( + attrs, + "completedAt", + $"V2CompletedMapping should set completedAt as ISO-8601 {ContractHint}" + ); + } + + /// + /// V1 completed-state onEntries: v1Completed=true, completedByVersion="1.0.0", + /// completedAt non-empty. + /// + public static void AssertV1Completed(JsonElement attrs) + { + JsonElementAssertions.AssertPropertyTrue( + attrs, + "v1Completed", + $"V1CompletedMapping should set v1Completed=true {ContractHint}" + ); + JsonElementAssertions.AssertPropertyString( + attrs, + "completedByVersion", + "1.0.0", + $"V1CompletedMapping should set completedByVersion='1.0.0' {ContractHint}" + ); + JsonElementAssertions.AssertPropertyNonEmptyString( + attrs, + "completedAt", + $"V1CompletedMapping should set completedAt as ISO-8601 {ContractHint}" + ); + } + + /// + /// Negative assertion: v1-specific flags must NOT be present on a v2 instance. + /// + public static void AssertNoV1Flags(JsonElement attrs) + { + var hasV1Completed = attrs.TryGetProperty("v1Completed", out var v1El) + && v1El.ValueKind == JsonValueKind.True; + Assert.False( + hasV1Completed, + $"v1Completed should NOT be true on a v2-path instance {ContractHint}" + ); + } + + /// + /// Negative assertion: review flags must NOT be present on a v1 instance + /// (review-state does not exist in v1). + /// + public static void AssertNoReviewFlags(JsonElement attrs) + { + var hasReviewExecuted = attrs.TryGetProperty("reviewExecuted", out var re) + && re.ValueKind == JsonValueKind.True; + Assert.False( + hasReviewExecuted, + $"reviewExecuted should NOT be true on a v1-path instance (review-state absent in v1) {ContractHint}" + ); + + var hasReviewAt = attrs.TryGetProperty("reviewAt", out var ra) + && ra.ValueKind == JsonValueKind.String + && !string.IsNullOrWhiteSpace(ra.GetString()); + Assert.False( + hasReviewAt, + $"reviewAt should NOT be present on a v1-path instance {ContractHint}" + ); + } +} diff --git a/tests/Core/Tests/version-consistency-test-workflow/VersionConsistencyScenarioActions.cs b/tests/Core/Tests/version-consistency-test-workflow/VersionConsistencyScenarioActions.cs new file mode 100644 index 0000000..2fc5ab2 --- /dev/null +++ b/tests/Core/Tests/version-consistency-test-workflow/VersionConsistencyScenarioActions.cs @@ -0,0 +1,95 @@ +using Core.IntegrationTests.Helpers; + +namespace Core.IntegrationTests.Tests.VersionConsistencyTestWorkflow; + +/// +/// Scenario actions for version-consistency-test-workflow. +/// Encapsulates instance start and state transitions for both v1 and v2 paths. +/// +public sealed class VersionConsistencyScenarioActions +{ + private readonly WorkflowInstanceTestHelper _wf; + private readonly TimeSpan _timeout; + + public VersionConsistencyScenarioActions(WorkflowInstanceTestHelper wf, TimeSpan timeout) + { + _wf = wf; + _timeout = timeout; + } + + /// + /// Starts an instance and waits for auto transition to land on processing-state. + /// On v2 path: init-state → (auto) → processing-state. + /// + public async Task StartAndWaitForProcessingAsync(string? testId = null) + { + var id = await _wf.StartInstanceIdAsync( + new + { + key = WorkflowInstanceTestHelper.UniqueInstanceKey("version-consistency"), + tags = new[] { "integration-test", "version-consistency" }, + attributes = new { testId = testId ?? $"vc-{Guid.NewGuid():N}" }, + } + ); + await _wf.WaitForStateAsync(id, "processing-state", _timeout); + return id; + } + + /// + /// Starts an instance with a specific key and waits for processing-state. + /// Used for idempotent start tests. + /// + public async Task StartWithKeyAndWaitForProcessingAsync(string key, string? testId = null) + { + var id = await _wf.StartInstanceIdAsync( + new + { + key, + tags = new[] { "integration-test", "version-consistency" }, + attributes = new { testId = testId ?? $"vc-{Guid.NewGuid():N}" }, + } + ); + await _wf.WaitForStateAsync(id, "processing-state", _timeout); + return id; + } + + /// + /// Runs complete-processing transition. On v2 path lands on review-state. + /// + public async Task AdvanceToReviewAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, "complete-processing", headers: null); + await _wf.WaitForStateAsync(instanceId, "review-state", _timeout); + } + + /// + /// Runs approve-review transition → completed-state. + /// + public async Task ApproveReviewAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, "approve-review", headers: null); + await _wf.WaitForStateAsync(instanceId, "completed-state", _timeout); + } + + /// + /// Full v2 happy path: start → processing → review → completed. + /// Returns instance id. + /// + public async Task RunFullV2HappyPathAsync(string? testId = null) + { + var id = await StartAndWaitForProcessingAsync(testId); + await AdvanceToReviewAsync(id); + await ApproveReviewAsync(id); + return id; + } + + /// + /// V1 path: complete-processing → directly completed-state (no review). + /// Only works when instance was started on v1.0.0. + /// + public async Task CompleteV1PathAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, "complete-processing", headers: null); + await _wf.WaitForStateAsync(instanceId, "completed-state", _timeout); + } +} diff --git a/tests/Core/Tests/version-consistency-test-workflow/VersionConsistencyTestWorkflowTests.cs b/tests/Core/Tests/version-consistency-test-workflow/VersionConsistencyTestWorkflowTests.cs new file mode 100644 index 0000000..fe9c55e --- /dev/null +++ b/tests/Core/Tests/version-consistency-test-workflow/VersionConsistencyTestWorkflowTests.cs @@ -0,0 +1,280 @@ +using Core.IntegrationTests.Helpers; +using Core.IntegrationTests.Infrastructure; + +namespace Core.IntegrationTests.Tests.VersionConsistencyTestWorkflow; + +/// +/// Integration tests for version-consistency-test-workflow (v1.0.0 + v2.0.0). +/// Validates that workflow version changes do not affect existing instances and that new +/// instances follow the latest published version path. +/// +/// Coverage aligns with doc/integration-test-documentation.md "Group 9: Version Consistency". +/// +/// Layout: +/// +/// Scenario actions: +/// Data contract assertions: +/// +/// +public class VersionConsistencyTestWorkflowTests : IntegrationTestBase +{ + private const string WorkflowKey = "version-consistency-test-workflow"; + + private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(15); + + private readonly WorkflowInstanceTestHelper _wf; + private readonly VersionConsistencyScenarioActions _scenario; + + public VersionConsistencyTestWorkflowTests(VNextTestEnvironment environment) + : base(environment) + { + _wf = new WorkflowInstanceTestHelper(Api, WorkflowKey); + _scenario = new VersionConsistencyScenarioActions(_wf, DefaultTimeout); + } + + // ----------------------------------------------------------------------- + // A) V2 Happy Path — full flow + all data contract assertions + // ----------------------------------------------------------------------- + + [Fact] + public async Task V2HappyPath_FullFlow_DataContract_ReachesCompletedState() + { + var testId = $"v2-happy-{Guid.NewGuid():N}"; + var instanceId = await _scenario.StartAndWaitForProcessingAsync(testId); + + // Authoritative version pin: GET /instances/{id} → flowVersion must be 2.0.0 + // (latest published version; runtime always starts new instances on the latest). + var instanceBody = await _wf.GetInstanceBodyAsync(instanceId); + VersionConsistencyInstanceDataAssertions.AssertWorkflowVersion(instanceBody, "2.0.0"); + + // Init mapping contract (after start + auto transition) + var attrsAfterInit = await _wf.GetAttributesAsync(instanceId); + VersionConsistencyInstanceDataAssertions.AssertInitializedAttributes(attrsAfterInit, testId); + + // Advance to review + await _scenario.AdvanceToReviewAsync(instanceId); + + // Review mapping contract + var attrsAfterReview = await _wf.GetAttributesAsync(instanceId); + VersionConsistencyInstanceDataAssertions.AssertReviewExecuted(attrsAfterReview); + + // Complete + await _scenario.ApproveReviewAsync(instanceId); + + // Final state + status + var body = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal("completed-state", StateFunctionJson.ExtractStateName(body)); + Assert.Equal("C", StateFunctionJson.ExtractStatus(body)); + + // Completed mapping contract + negative v1 flags + var attrsCompleted = await _wf.GetAttributesAsync(instanceId); + VersionConsistencyInstanceDataAssertions.AssertV2Completed(attrsCompleted); + VersionConsistencyInstanceDataAssertions.AssertNoV1Flags(attrsCompleted); + + // Re-confirm version pin at completion: still 2.0.0 + var finalBody = await _wf.GetInstanceBodyAsync(instanceId); + VersionConsistencyInstanceDataAssertions.AssertWorkflowVersion(finalBody, "2.0.0"); + } + + // ----------------------------------------------------------------------- + // B) Parallel Instance Execution + // ----------------------------------------------------------------------- + + [Fact] + public async Task ParallelInstances_BothReachCompletedIndependently() + { + var idA = await _scenario.StartAndWaitForProcessingAsync("parallel-A"); + var idB = await _scenario.StartAndWaitForProcessingAsync("parallel-B"); + + await _scenario.AdvanceToReviewAsync(idA); + await _scenario.AdvanceToReviewAsync(idB); + + await _scenario.ApproveReviewAsync(idA); + await _scenario.ApproveReviewAsync(idB); + + await _wf.AssertStateAsync(idA, "completed-state"); + await _wf.AssertStateAsync(idB, "completed-state"); + } + + [Fact] + public async Task ParallelInstances_DifferentSpeeds_AWaitsWhileBCompletes() + { + var idA = await _scenario.StartAndWaitForProcessingAsync("speed-A"); + var idB = await _scenario.StartAndWaitForProcessingAsync("speed-B"); + + // B completes full path while A stays at processing + await _scenario.AdvanceToReviewAsync(idB); + await _scenario.ApproveReviewAsync(idB); + await _wf.AssertStateAsync(idB, "completed-state"); + + // A is still at processing + await _wf.AssertStateAsync(idA, "processing-state"); + + // Now advance A + await _scenario.AdvanceToReviewAsync(idA); + await _scenario.ApproveReviewAsync(idA); + await _wf.AssertStateAsync(idA, "completed-state"); + } + + // ----------------------------------------------------------------------- + // C) Idempotent + Version Pinning (CRITICAL) + // ----------------------------------------------------------------------- + + /// + /// CRITICAL: When a v1 instance was started with a specific key and v2 is now the latest + /// published version, sending start POST with the same key must: + /// 1. Return the SAME instance id (idempotent behavior) + /// 2. NOT upgrade the instance to v2 (version pinning preserved) + /// 3. The instance must remain on its v1 path (state and data unchanged) + /// + /// This proves that version pinning is NOT broken by idempotent start requests. + /// + [Fact] + public async Task IdempotentStart_V1Instance_RemainsOnV1Path_NotUpgradedToV2() + { + // Start instance with a known key (currently v2 is latest, but if v1 was started first + // via publish ordering, the instance is pinned to v1). + // In the integration test environment both versions are published. + // The idempotent behavior: second POST with same key returns same instance. + var uniqueKey = $"version-pin-idempotent-{Guid.NewGuid():N}"; + + var firstId = await _wf.StartInstanceIdAsync( + new + { + key = uniqueKey, + tags = new[] { "integration-test", "version-consistency", "idempotent" }, + attributes = new { testId = "idempotent-version-pin" }, + } + ); + + await _wf.WaitForStateAsync(firstId, "processing-state", DefaultTimeout); + + // Second start with same key — must return same id + var secondId = await _wf.StartInstanceIdAsync( + new + { + key = uniqueKey, + tags = new[] { "integration-test", "version-consistency", "idempotent" }, + attributes = new { testId = "idempotent-version-pin" }, + } + ); + + Assert.Equal(firstId, secondId); + + // Instance must still be on processing-state (not re-initialized or upgraded) + await _wf.AssertStateAsync(firstId, "processing-state"); + + // Authoritative version pin check: flowVersion BEFORE second start + var bodyBefore = await _wf.GetInstanceBodyAsync(firstId); + var versionBefore = VersionConsistencyInstanceDataAssertions.ExtractWorkflowSemver(bodyBefore); + + // Authoritative version pin check: flowVersion AFTER second start must be unchanged + var bodyAfter = await _wf.GetInstanceBodyAsync(secondId); + var versionAfter = VersionConsistencyInstanceDataAssertions.ExtractWorkflowSemver(bodyAfter); + + Assert.Equal(versionBefore, versionAfter); + + // Data must reflect the original initialization (not a v2 re-start) + var attrs = await _wf.GetAttributesAsync(firstId); + VersionConsistencyInstanceDataAssertions.AssertInitializedAttributes( + attrs, + "idempotent-version-pin" + ); + } + + // ----------------------------------------------------------------------- + // D) Version-Driven Path Selection + // Authoritative source: GET /instances/{id} → flowVersion. + // The runtime always pins new instances to the LATEST published version, + // so in this test environment new instances land on v2.0.0. + // These tests verify the version-driven path: assert the version first, + // then drive the appropriate workflow path based on it. + // ----------------------------------------------------------------------- + + /// + /// Drive workflow based on actual flowVersion read from GET /instances/{id}. + /// Both v1 and v2 paths are validated as branches; whichever the runtime picks is verified end-to-end. + /// In the current test environment, v2 is the latest → instance is pinned to v2.0.0. + /// + [Fact] + public async Task PathSelection_DrivenByActualFlowVersion_ReachesVersionAppropriateCompletedState() + { + var instanceId = await _scenario.StartAndWaitForProcessingAsync("path-selection-test"); + + // Authoritative version source + var instanceBody = await _wf.GetInstanceBodyAsync(instanceId); + var workflowVersion = + VersionConsistencyInstanceDataAssertions.ExtractWorkflowSemver(instanceBody); + + await _wf.RunTransitionAsync(instanceId, "complete-processing", headers: null); + + if (workflowVersion == "2.0.0") + { + await _wf.WaitForStateAsync(instanceId, "review-state", DefaultTimeout); + await _scenario.ApproveReviewAsync(instanceId); + + var attrs = await _wf.GetAttributesAsync(instanceId); + VersionConsistencyInstanceDataAssertions.AssertV2Completed(attrs); + VersionConsistencyInstanceDataAssertions.AssertReviewExecuted(attrs); + VersionConsistencyInstanceDataAssertions.AssertNoV1Flags(attrs); + } + else if (workflowVersion == "1.0.0") + { + await _wf.WaitForStateAsync(instanceId, "completed-state", DefaultTimeout); + + var attrs = await _wf.GetAttributesAsync(instanceId); + VersionConsistencyInstanceDataAssertions.AssertV1Completed(attrs); + VersionConsistencyInstanceDataAssertions.AssertNoReviewFlags(attrs); + } + else + { + Assert.Fail( + $"Unexpected workflow version '{workflowVersion}'. Only '1.0.0' and '2.0.0' are defined." + ); + } + + // Re-confirm version is unchanged after completion (no implicit upgrade). + var finalBody = await _wf.GetInstanceBodyAsync(instanceId); + VersionConsistencyInstanceDataAssertions.AssertWorkflowVersion(finalBody, workflowVersion); + } + + /// + /// Verifies that the instance's pinned flowVersion never changes during its lifecycle, + /// even after multiple state transitions. The version observed at start must equal the + /// version at every subsequent step. + /// + [Fact] + public async Task FlowVersion_StableAcrossLifecycle_NeverChanges() + { + var instanceId = await _scenario.StartAndWaitForProcessingAsync("version-stability"); + + var bodyAtProcessing = await _wf.GetInstanceBodyAsync(instanceId); + var versionAtProcessing = + VersionConsistencyInstanceDataAssertions.ExtractWorkflowSemver(bodyAtProcessing); + + await _wf.RunTransitionAsync(instanceId, "complete-processing", headers: null); + + if (versionAtProcessing == "2.0.0") + { + await _wf.WaitForStateAsync(instanceId, "review-state", DefaultTimeout); + + var bodyAtReview = await _wf.GetInstanceBodyAsync(instanceId); + VersionConsistencyInstanceDataAssertions.AssertWorkflowVersion( + bodyAtReview, + versionAtProcessing + ); + + await _scenario.ApproveReviewAsync(instanceId); + } + else + { + await _wf.WaitForStateAsync(instanceId, "completed-state", DefaultTimeout); + } + + var bodyAtCompleted = await _wf.GetInstanceBodyAsync(instanceId); + VersionConsistencyInstanceDataAssertions.AssertWorkflowVersion( + bodyAtCompleted, + versionAtProcessing + ); + } +} From c7eb5d6079c0cb57dae5bf74e9868b1b94fe0a48 Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Fri, 8 May 2026 14:28:38 +0300 Subject: [PATCH 21/24] error boundary test coverage is increased. --- core.link.json | 2 +- .../error-boundary-test-workflow.json | 437 +++++++++++++----- .../error-boundary/src/ErrorHttpMapping.csx | 12 +- .../error-boundary/src/IgnoreErrorMapping.csx | 5 +- .../error-boundary/src/LogOnlyMapping.csx | 9 +- .../src/PriorityConfirmMapping.csx | 27 ++ .../src/RetryConfirmMapping.csx | 24 + .../src/StateLevelConfirmMapping.csx | 28 ++ .../error-boundary/src/ThrowErrorMapping.csx | 2 +- core/Workflows/flow-types/core-flow-test.json | 8 +- docs/integration-test-documentation.md | 35 +- .../ErrorBoundaryInstanceDataAssertions.cs | 67 +++ .../ErrorBoundaryTestWorkflowTests.cs | 136 ++++++ 13 files changed, 638 insertions(+), 154 deletions(-) create mode 100644 core/Workflows/error-boundary/src/PriorityConfirmMapping.csx create mode 100644 core/Workflows/error-boundary/src/RetryConfirmMapping.csx create mode 100644 core/Workflows/error-boundary/src/StateLevelConfirmMapping.csx create mode 100644 tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryInstanceDataAssertions.cs create mode 100644 tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryTestWorkflowTests.cs diff --git a/core.link.json b/core.link.json index f47204b..334907e 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-05-08T06:33:38.552Z" + "importedAt": "2026-05-08T10:54:41.263Z" } \ No newline at end of file diff --git a/core/Workflows/error-boundary/error-boundary-test-workflow.json b/core/Workflows/error-boundary/error-boundary-test-workflow.json index b6b38d2..8bdbb3f 100644 --- a/core/Workflows/error-boundary/error-boundary-test-workflow.json +++ b/core/Workflows/error-boundary/error-boundary-test-workflow.json @@ -1,56 +1,43 @@ { + "$schema": "../../schemas/workflow-definition.schema.json", "key": "error-boundary-test-workflow", + "version": "1.0.0", + "domain": "core", "flow": "sys-flows", "flowVersion": "1.0.0", - "domain": "core", - "version": "1.0.0", "tags": [ "integration-test", "error-boundary", "retry", - "abort", "ignore", "log", "rollback", - "log-action", - "retry-policy", - "priority", - "task-level-boundary", - "state-level-boundary", - "global-boundary", "notify", - "notify-action", - "timeout-policy", - "error-types", - "error-codes" + "abort", + "priority" ], "attributes": { "type": "F", - "timeout": null, "labels": [ { "language": "en-US", - "label": "Error Boundary Integration Test" + "label": "Error Boundary Integration Test Workflow" } ], - "functions": [], - "features": [], - "extensions": [], - "sharedTransitions": [], "errorBoundary": { "onError": [ { "action": 0, - "priority": 100 + "errorCodes": [ + "*" + ], + "priority": 999 } - ], - "onTimeout": { - "action": 3 - } + ] }, "startTransition": { - "key": "start-error-boundary-test", - "target": "retry-test-state", + "key": "start-error-test", + "target": "init-state", "triggerType": 0, "versionStrategy": "Major", "labels": [ @@ -58,33 +45,18 @@ "language": "en-US", "label": "Start Error Boundary Test" } - ], - "onExecutionTasks": [ - { - "order": 1, - "task": { - "key": "error-script-task", - "domain": "core", - "version": "1.0.0", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/InitErrorTestMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBJbml0RXJyb3JUZXN0TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC50ZXN0SWQgPSBHdWlkLk5ld0d1aWQoKS5Ub1N0cmluZygpOwogICAgICAgIHJlc3VsdC5lcnJvclRlc3RTdGFydGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQuc3RhcnRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkluaXRFcnJvclRlc3RNYXBwaW5nOiBlcnJvciBib3VuZGFyeSBpbnRlZ3JhdGlvbiB0ZXN0IGluaXRpYWxpemVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" - } - } ] }, "states": [ { - "key": "retry-test-state", + "key": "init-state", "stateType": 1, "subType": 0, "versionStrategy": "Major", "labels": [ { "language": "en-US", - "label": "Retry Test State" + "label": "Initialize Error Test" } ], "view": null, @@ -93,14 +65,63 @@ { "order": 1, "task": { - "key": "error-http-task", + "key": "error-script-task", "domain": "core", "version": "1.0.0", "flow": "sys-tasks" }, "mapping": { - "location": "./src/ErrorHttpMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nLkZ1bmN0aW9uczsKCnB1YmxpYyBjbGFzcyBFcnJvckh0dHBNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBodHRwVGFzayA9ICh0YXNrIGFzIEh0dHBUYXNrKSE7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgc3RyaW5nIGJhc2VVcmwgPSBHZXRDb25maWdWYWx1ZSgiTW9ja2xhYkJhc2VVcmwiKTsKICAgICAgICBodHRwVGFzay5VcmwgPSBodHRwVGFzay5VcmwuUmVwbGFjZSgie01vY2tsYWJCYXNlVXJsfSIsIGJhc2VVcmwpOwogICAgICAgIHZhciBib2R5ID0gbmV3CiAgICAgICAgewogICAgICAgICAgICBzb3VyY2UgPSAiZXJyb3ItYm91bmRhcnktdGVzdCIsCiAgICAgICAgICAgIHRlc3RJZCA9IEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSA/IGRhdGEudGVzdElkIDogbnVsbCwKICAgICAgICAgICAgdGltZXN0YW1wID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIikKICAgICAgICB9OwogICAgICAgIGh0dHBUYXNrLlNldEJvZHkoYm9keSk7CiAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZSgpOwogICAgfQoKICAgIHB1YmxpYyBhc3luYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICB2YXIgdGFza1Jlc3BvbnNlID0gY29udGV4dC5Cb2R5OwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJlcnJvclRlc3RTdGFydGVkIikpIHJlc3VsdC5lcnJvclRlc3RTdGFydGVkID0gZGF0YS5lcnJvclRlc3RTdGFydGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICByZXN1bHQuaHR0cEVycm9ySGFuZGxlZCA9IHRydWU7CiAgICAgICAgaWYgKHRhc2tSZXNwb25zZSAhPSBudWxsKQogICAgICAgIHsKICAgICAgICAgICAgcmVzdWx0Lmh0dHBTdGF0dXNDb2RlID0gdGFza1Jlc3BvbnNlLnN0YXR1c0NvZGU7CiAgICAgICAgICAgIHJlc3VsdC5odHRwSXNTdWNjZXNzID0gdGFza1Jlc3BvbnNlLmlzU3VjY2VzczsKICAgICAgICAgICAgaWYgKHRhc2tSZXNwb25zZS5kYXRhICE9IG51bGwpCiAgICAgICAgICAgICAgICByZXN1bHQuaHR0cFJlc3BvbnNlRGF0YSA9IHRhc2tSZXNwb25zZS5kYXRhOwogICAgICAgIH0KICAgICAgICBMb2dJbmZvcm1hdGlvbigiRXJyb3JIdHRwTWFwcGluZzogb3V0cHV0IGFmdGVyIEhUVFAgY2FsbCAoZXJyb3IgYm91bmRhcnkgbWF5IGhhdmUgcmV0cmllZCkiKTsKICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9OwogICAgfQp9Cg==" + "location": "./src/InitErrorTestMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBJbml0RXJyb3JUZXN0TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC50ZXN0SWQgPSBHdWlkLk5ld0d1aWQoKS5Ub1N0cmluZygpOwogICAgICAgIHJlc3VsdC5lcnJvclRlc3RTdGFydGVkID0gdHJ1ZTsKICAgICAgICByZXN1bHQuc3RhcnRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkluaXRFcnJvclRlc3RNYXBwaW5nOiBlcnJvciBib3VuZGFyeSBpbnRlZ3JhdGlvbiB0ZXN0IGluaXRpYWxpemVkIik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "auto-to-task-retry", + "target": "task-retry-state", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to Task Retry" + } + ], + "rule": { + "location": "./src/AlwaysTrueRule.csx", + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" + }, + "onExecutionTasks": [] + } + ] + }, + { + "key": "task-retry-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Task-Level Retry Action Test" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "error-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/ThrowErrorMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFRocm93RXJyb3JNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0aHJvdyBuZXcgSW52YWxpZE9wZXJhdGlvbkV4Y2VwdGlvbigiSW50ZW50aW9uYWwgdGVzdCBlcnJvciBmb3IgZXJyb3IgYm91bmRhcnkiKTsKICAgIH0KfQo=" }, "errorBoundary": { "onError": [ @@ -109,25 +130,48 @@ "priority": 10, "retryPolicy": { "maxRetries": 2, - "initialDelay": "PT2S", - "backoffType": 0 + "initialDelay": "PT1S", + "backoffType": 1, + "backoffMultiplier": 2, + "maxDelay": "PT10S", + "useJitter": false } + }, + { + "action": 3, + "errorCodes": [ + "*" + ], + "priority": 999 } ] } + }, + { + "order": 2, + "task": { + "key": "error-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/RetryConfirmMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBSZXRyeUNvbmZpcm1NYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZXJyb3JUZXN0U3RhcnRlZCIpKSByZXN1bHQuZXJyb3JUZXN0U3RhcnRlZCA9IGRhdGEuZXJyb3JUZXN0U3RhcnRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRBdCIpKSByZXN1bHQuc3RhcnRlZEF0ID0gZGF0YS5zdGFydGVkQXQ7CiAgICAgICAgcmVzdWx0LnJldHJ5SGFuZGxlZCA9IHRydWU7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlJldHJ5Q29uZmlybU1hcHBpbmc6IHJldHJ5IGJvdW5kYXJ5IGV4aGF1c3RlZCArIGlnbm9yZSBmYWxsYmFjayBjb25maXJtZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" + } } ], "onExits": [], "transitions": [ { - "key": "auto-to-ignore-test", - "target": "ignore-test-state", + "key": "auto-to-task-ignore", + "target": "task-ignore-state", "triggerType": 1, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Auto to Ignore Test" + "label": "Auto to Task Ignore" } ], "rule": { @@ -139,32 +183,18 @@ ] }, { - "key": "ignore-test-state", + "key": "task-ignore-state", "stateType": 2, "subType": 0, - "versionStrategy": "Minor", + "versionStrategy": "Major", "labels": [ { "language": "en-US", - "label": "Ignore Test State" + "label": "Task-Level Ignore Action Test" } ], "view": null, "subFlow": null, - "errorBoundary": { - "onError": [ - { - "action": 3, - "priority": 10, - "errorTypes": [ - "System.InvalidOperationException" - ], - "errorCodes": [ - "*" - ] - } - ] - }, "onEntries": [ { "order": 1, @@ -176,7 +206,15 @@ }, "mapping": { "location": "./src/ThrowErrorMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFRocm93RXJyb3JNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCJJbnRlbnRpb25hbCB0ZXN0IGVycm9yIGZvciBlcnJvciBib3VuZGFyeSIpOwogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFRocm93RXJyb3JNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0aHJvdyBuZXcgSW52YWxpZE9wZXJhdGlvbkV4Y2VwdGlvbigiSW50ZW50aW9uYWwgdGVzdCBlcnJvciBmb3IgZXJyb3IgYm91bmRhcnkiKTsKICAgIH0KfQo=" + }, + "errorBoundary": { + "onError": [ + { + "action": 3, + "priority": 10 + } + ] } }, { @@ -189,21 +227,21 @@ }, "mapping": { "location": "./src/IgnoreErrorMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBJZ25vcmVFcnJvck1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJlcnJvclRlc3RTdGFydGVkIikpIHJlc3VsdC5lcnJvclRlc3RTdGFydGVkID0gZGF0YS5lcnJvclRlc3RTdGFydGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiaHR0cEVycm9ySGFuZGxlZCIpKSByZXN1bHQuaHR0cEVycm9ySGFuZGxlZCA9IGRhdGEuaHR0cEVycm9ySGFuZGxlZDsKICAgICAgICByZXN1bHQuZXJyb3JJZ25vcmVkID0gdHJ1ZTsKICAgICAgICBMb2dJbmZvcm1hdGlvbigiSWdub3JlRXJyb3JNYXBwaW5nOiBjb250aW51YXRpb24gYWZ0ZXIgaWdub3JlIHBvbGljeSIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBJZ25vcmVFcnJvck1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJlcnJvclRlc3RTdGFydGVkIikpIHJlc3VsdC5lcnJvclRlc3RTdGFydGVkID0gZGF0YS5lcnJvclRlc3RTdGFydGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInJldHJ5SGFuZGxlZCIpKSByZXN1bHQucmV0cnlIYW5kbGVkID0gZGF0YS5yZXRyeUhhbmRsZWQ7CiAgICAgICAgcmVzdWx0LmVycm9ySWdub3JlZCA9IHRydWU7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIklnbm9yZUVycm9yTWFwcGluZzogZXJyb3Igd2FzIGlnbm9yZWQsIGNvbnRpbnVhdGlvbiBjb25maXJtZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } ], "onExits": [], "transitions": [ { - "key": "auto-to-rollback-from-ignore", - "target": "rollback-test-state", + "key": "auto-to-task-log", + "target": "task-log-state", "triggerType": 1, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Auto to Rollback from Ignore" + "label": "Auto to Task Log" } ], "rule": { @@ -215,27 +253,18 @@ ] }, { - "key": "rollback-test-state", + "key": "task-log-state", "stateType": 2, "subType": 0, "versionStrategy": "Major", "labels": [ { "language": "en-US", - "label": "Rollback Action Test" + "label": "Task-Level Log Action Test" } ], "view": null, "subFlow": null, - "errorBoundary": { - "onError": [ - { - "action": 2, - "priority": 10, - "transition": "auto-to-log-from-rollback" - } - ] - }, "onEntries": [ { "order": 1, @@ -247,7 +276,16 @@ }, "mapping": { "location": "./src/ThrowErrorMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFRocm93RXJyb3JNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCJJbnRlbnRpb25hbCB0ZXN0IGVycm9yIGZvciBlcnJvciBib3VuZGFyeSIpOwogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFRocm93RXJyb3JNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0aHJvdyBuZXcgSW52YWxpZE9wZXJhdGlvbkV4Y2VwdGlvbigiSW50ZW50aW9uYWwgdGVzdCBlcnJvciBmb3IgZXJyb3IgYm91bmRhcnkiKTsKICAgIH0KfQo=" + }, + "errorBoundary": { + "onError": [ + { + "action": 5, + "priority": 10, + "logOnly": true + } + ] } }, { @@ -259,22 +297,22 @@ "flow": "sys-tasks" }, "mapping": { - "location": "./src/RollbackMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFJvbGxiYWNrTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKQogICAgICAgICAgICByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZXRyeUNvbXBsZXRlZCIpKQogICAgICAgICAgICByZXN1bHQucmV0cnlDb21wbGV0ZWQgPSBkYXRhLnJldHJ5Q29tcGxldGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZXJyb3JJZ25vcmVkIikpCiAgICAgICAgICAgIHJlc3VsdC5lcnJvcklnbm9yZWQgPSBkYXRhLmVycm9ySWdub3JlZDsKCiAgICAgICAgcmVzdWx0LnJvbGxiYWNrRXhlY3V0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC5yb2xsYmFja0F0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCJSb2xsYmFja01hcHBpbmcgY29tcGxldGVkIC0gcm9sbGJhY2sgYWN0aW9uIHRlc3QiKTsKCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" + "location": "./src/LogOnlyMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBMb2dPbmx5TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImVycm9yVGVzdFN0YXJ0ZWQiKSkgcmVzdWx0LmVycm9yVGVzdFN0YXJ0ZWQgPSBkYXRhLmVycm9yVGVzdFN0YXJ0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJzdGFydGVkQXQiKSkgcmVzdWx0LnN0YXJ0ZWRBdCA9IGRhdGEuc3RhcnRlZEF0OwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicmV0cnlIYW5kbGVkIikpIHJlc3VsdC5yZXRyeUhhbmRsZWQgPSBkYXRhLnJldHJ5SGFuZGxlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImVycm9ySWdub3JlZCIpKSByZXN1bHQuZXJyb3JJZ25vcmVkID0gZGF0YS5lcnJvcklnbm9yZWQ7CiAgICAgICAgcmVzdWx0LmxvZ0hhbmRsZWQgPSB0cnVlOwogICAgICAgIExvZ0luZm9ybWF0aW9uKCJMb2dPbmx5TWFwcGluZzogbG9nLW9ubHkgYWN0aW9uIGNvbmZpcm1lZCwgZmxvdyBjb250aW51ZXMiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } ], "onExits": [], "transitions": [ { - "key": "auto-to-log-from-rollback", - "target": "log-test-state", + "key": "auto-to-priority-rules", + "target": "priority-rules-state", "triggerType": 1, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Auto to Log from Rollback" + "label": "Auto to Priority Rules" } ], "rule": { @@ -286,26 +324,18 @@ ] }, { - "key": "log-test-state", + "key": "priority-rules-state", "stateType": 2, "subType": 0, "versionStrategy": "Major", "labels": [ { "language": "en-US", - "label": "Log Action Test" + "label": "Priority-Based Multiple Rules Test" } ], "view": null, "subFlow": null, - "errorBoundary": { - "onError": [ - { - "action": 5, - "priority": 10 - } - ] - }, "onEntries": [ { "order": 1, @@ -317,7 +347,25 @@ }, "mapping": { "location": "./src/ThrowErrorMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFRocm93RXJyb3JNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCJJbnRlbnRpb25hbCB0ZXN0IGVycm9yIGZvciBlcnJvciBib3VuZGFyeSIpOwogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFRocm93RXJyb3JNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0aHJvdyBuZXcgSW52YWxpZE9wZXJhdGlvbkV4Y2VwdGlvbigiSW50ZW50aW9uYWwgdGVzdCBlcnJvciBmb3IgZXJyb3IgYm91bmRhcnkiKTsKICAgIH0KfQo=" + }, + "errorBoundary": { + "onError": [ + { + "action": 3, + "errorTypes": [ + "InvalidOperationException" + ], + "priority": 1 + }, + { + "action": 0, + "errorCodes": [ + "*" + ], + "priority": 999 + } + ] } }, { @@ -329,22 +377,22 @@ "flow": "sys-tasks" }, "mapping": { - "location": "./src/LogOnlyMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBMb2dPbmx5TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImVycm9yVGVzdFN0YXJ0ZWQiKSkgcmVzdWx0LmVycm9yVGVzdFN0YXJ0ZWQgPSBkYXRhLmVycm9yVGVzdFN0YXJ0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJodHRwRXJyb3JIYW5kbGVkIikpIHJlc3VsdC5odHRwRXJyb3JIYW5kbGVkID0gZGF0YS5odHRwRXJyb3JIYW5kbGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZXJyb3JJZ25vcmVkIikpIHJlc3VsdC5lcnJvcklnbm9yZWQgPSBkYXRhLmVycm9ySWdub3JlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInJvbGxiYWNrRXhlY3V0ZWQiKSkgcmVzdWx0LnJvbGxiYWNrRXhlY3V0ZWQgPSBkYXRhLnJvbGxiYWNrRXhlY3V0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyb2xsYmFja0F0IikpIHJlc3VsdC5yb2xsYmFja0F0ID0gZGF0YS5yb2xsYmFja0F0OwogICAgICAgIHJlc3VsdC5sb2dPbmx5RXhlY3V0ZWQgPSB0cnVlOwogICAgICAgIExvZ0luZm9ybWF0aW9uKCJMb2dPbmx5TWFwcGluZzogbG9nLW9ubHkgcGF0aCBleGVjdXRlZCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" + "location": "./src/PriorityConfirmMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBQcmlvcml0eUNvbmZpcm1NYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZXJyb3JUZXN0U3RhcnRlZCIpKSByZXN1bHQuZXJyb3JUZXN0U3RhcnRlZCA9IGRhdGEuZXJyb3JUZXN0U3RhcnRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRBdCIpKSByZXN1bHQuc3RhcnRlZEF0ID0gZGF0YS5zdGFydGVkQXQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZXRyeUhhbmRsZWQiKSkgcmVzdWx0LnJldHJ5SGFuZGxlZCA9IGRhdGEucmV0cnlIYW5kbGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZXJyb3JJZ25vcmVkIikpIHJlc3VsdC5lcnJvcklnbm9yZWQgPSBkYXRhLmVycm9ySWdub3JlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImxvZ0hhbmRsZWQiKSkgcmVzdWx0LmxvZ0hhbmRsZWQgPSBkYXRhLmxvZ0hhbmRsZWQ7CiAgICAgICAgcmVzdWx0LnByaW9yaXR5UnVsZUFwcGxpZWQgPSB0cnVlOwogICAgICAgIExvZ0luZm9ybWF0aW9uKCJQcmlvcml0eUNvbmZpcm1NYXBwaW5nOiBwcmlvcml0eS1iYXNlZCBydWxlIHNlbGVjdGlvbiBjb25maXJtZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } ], "onExits": [], "transitions": [ { - "key": "auto-to-completed-from-log", - "target": "notify-test-state", + "key": "auto-to-state-level", + "target": "state-level-test-state", "triggerType": 1, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Auto to Completed from Log" + "label": "Auto to State Level Test" } ], "rule": { @@ -356,14 +404,14 @@ ] }, { - "key": "notify-test-state", + "key": "state-level-test-state", "stateType": 2, "subType": 0, "versionStrategy": "Major", "labels": [ { "language": "en-US", - "label": "Notify Action Test" + "label": "State-Level Error Boundary Test" } ], "view": null, @@ -371,9 +419,8 @@ "errorBoundary": { "onError": [ { - "action": 4, - "priority": 10, - "transition": "auto-to-completed-from-notify" + "action": 3, + "priority": 10 } ] }, @@ -388,7 +435,7 @@ }, "mapping": { "location": "./src/ThrowErrorMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFRocm93RXJyb3JNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCJJbnRlbnRpb25hbCB0ZXN0IGVycm9yIGZvciBlcnJvciBib3VuZGFyeSIpOwogICAgfQp9Cg==" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFRocm93RXJyb3JNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0aHJvdyBuZXcgSW52YWxpZE9wZXJhdGlvbkV4Y2VwdGlvbigiSW50ZW50aW9uYWwgdGVzdCBlcnJvciBmb3IgZXJyb3IgYm91bmRhcnkiKTsKICAgIH0KfQo=" } }, { @@ -400,22 +447,22 @@ "flow": "sys-tasks" }, "mapping": { - "location": "./src/NotifyMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIE5vdGlmeU1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKCiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkKICAgICAgICAgICAgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicmV0cnlDb21wbGV0ZWQiKSkKICAgICAgICAgICAgcmVzdWx0LnJldHJ5Q29tcGxldGVkID0gZGF0YS5yZXRyeUNvbXBsZXRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImVycm9ySWdub3JlZCIpKQogICAgICAgICAgICByZXN1bHQuZXJyb3JJZ25vcmVkID0gZGF0YS5lcnJvcklnbm9yZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyb2xsYmFja0V4ZWN1dGVkIikpCiAgICAgICAgICAgIHJlc3VsdC5yb2xsYmFja0V4ZWN1dGVkID0gZGF0YS5yb2xsYmFja0V4ZWN1dGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicm9sbGJhY2tBdCIpKQogICAgICAgICAgICByZXN1bHQucm9sbGJhY2tBdCA9IGRhdGEucm9sbGJhY2tBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImxvZ0V4ZWN1dGVkIikpCiAgICAgICAgICAgIHJlc3VsdC5sb2dFeGVjdXRlZCA9IGRhdGEubG9nRXhlY3V0ZWQ7CgogICAgICAgIHJlc3VsdC5ub3RpZnlFeGVjdXRlZCA9IHRydWU7CiAgICAgICAgcmVzdWx0Lm5vdGlmeUF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCJOb3RpZnlNYXBwaW5nIGNvbXBsZXRlZCAtIG5vdGlmeSBhY3Rpb24gdGVzdCIpOwoKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" + "location": "./src/StateLevelConfirmMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBTdGF0ZUxldmVsQ29uZmlybU1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKSByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJlcnJvclRlc3RTdGFydGVkIikpIHJlc3VsdC5lcnJvclRlc3RTdGFydGVkID0gZGF0YS5lcnJvclRlc3RTdGFydGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInJldHJ5SGFuZGxlZCIpKSByZXN1bHQucmV0cnlIYW5kbGVkID0gZGF0YS5yZXRyeUhhbmRsZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJlcnJvcklnbm9yZWQiKSkgcmVzdWx0LmVycm9ySWdub3JlZCA9IGRhdGEuZXJyb3JJZ25vcmVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAibG9nSGFuZGxlZCIpKSByZXN1bHQubG9nSGFuZGxlZCA9IGRhdGEubG9nSGFuZGxlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInByaW9yaXR5UnVsZUFwcGxpZWQiKSkgcmVzdWx0LnByaW9yaXR5UnVsZUFwcGxpZWQgPSBkYXRhLnByaW9yaXR5UnVsZUFwcGxpZWQ7CiAgICAgICAgcmVzdWx0LnN0YXRlTGV2ZWxIYW5kbGVkID0gdHJ1ZTsKICAgICAgICBMb2dJbmZvcm1hdGlvbigiU3RhdGVMZXZlbENvbmZpcm1NYXBwaW5nOiBzdGF0ZS1sZXZlbCBlcnJvciBib3VuZGFyeSBjb25maXJtZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } ], "onExits": [], "transitions": [ { - "key": "auto-to-completed-from-notify", - "target": "completed-state", + "key": "auto-to-notify", + "target": "notify-test-state", "triggerType": 1, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Auto to Completed from Notify" + "label": "Auto to Notify Test" } ], "rule": { @@ -427,31 +474,169 @@ ] }, { - "key": "completed-state", - "stateType": 3, - "subType": 1, + "key": "notify-test-state", + "stateType": 2, + "subType": 0, "versionStrategy": "Major", "labels": [ { "language": "en-US", - "label": "Success Final" + "label": "Task-Level Notify Action Test" } ], "view": null, "subFlow": null, - "onEntries": [], + "onEntries": [ + { + "order": 1, + "task": { + "key": "error-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/ThrowErrorMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFRocm93RXJyb3JNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0aHJvdyBuZXcgSW52YWxpZE9wZXJhdGlvbkV4Y2VwdGlvbigiSW50ZW50aW9uYWwgdGVzdCBlcnJvciBmb3IgZXJyb3IgYm91bmRhcnkiKTsKICAgIH0KfQo=" + }, + "errorBoundary": { + "onError": [ + { + "action": 4, + "priority": 10, + "transition": "notify-redirect" + } + ] + } + } + ], "onExits": [], - "transitions": [] + "transitions": [ + { + "key": "notify-redirect", + "target": "rollback-test-state", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Notify Redirect to Rollback" + } + ] + } + ] + }, + { + "key": "rollback-test-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Task-Level Rollback Action Test" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "error-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/ThrowErrorMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFRocm93RXJyb3JNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0aHJvdyBuZXcgSW52YWxpZE9wZXJhdGlvbkV4Y2VwdGlvbigiSW50ZW50aW9uYWwgdGVzdCBlcnJvciBmb3IgZXJyb3IgYm91bmRhcnkiKTsKICAgIH0KfQo=" + }, + "errorBoundary": { + "onError": [ + { + "action": 2, + "priority": 10, + "transition": "rollback-redirect" + } + ] + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "rollback-redirect", + "target": "global-abort-test-state", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Rollback Redirect to Global Abort" + } + ] + } + ] + }, + { + "key": "global-abort-test-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Global-Level Abort Action Test" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "error-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/ThrowErrorMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFRocm93RXJyb3JNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0aHJvdyBuZXcgSW52YWxpZE9wZXJhdGlvbkV4Y2VwdGlvbigiSW50ZW50aW9uYWwgdGVzdCBlcnJvciBmb3IgZXJyb3IgYm91bmRhcnkiKTsKICAgIH0KfQo=" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "auto-to-completed", + "target": "completed-state", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to Completed (unreachable - global abort fires first)" + } + ], + "rule": { + "location": "./src/AlwaysTrueRule.csx", + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" + }, + "onExecutionTasks": [] + } + ] }, { - "key": "error-final", + "key": "completed-state", "stateType": 3, - "subType": 2, + "subType": 1, "versionStrategy": "Major", "labels": [ { "language": "en-US", - "label": "Error Final" + "label": "Completed State (Success)" } ], "view": null, diff --git a/core/Workflows/error-boundary/src/ErrorHttpMapping.csx b/core/Workflows/error-boundary/src/ErrorHttpMapping.csx index ffc5847..2200775 100644 --- a/core/Workflows/error-boundary/src/ErrorHttpMapping.csx +++ b/core/Workflows/error-boundary/src/ErrorHttpMapping.csx @@ -11,8 +11,16 @@ public class ErrorHttpMapping : ScriptBase, IMapping { var httpTask = (task as HttpTask)!; var data = context.Instance.Data; - string baseUrl = GetConfigValue("MocklabBaseUrl"); - httpTask.Url = httpTask.Url.Replace("{MocklabBaseUrl}", baseUrl); + string baseUrl = null; + try { baseUrl = GetConfigValue("MocklabBaseUrl"); } catch { } + if (!string.IsNullOrEmpty(baseUrl)) + { + httpTask.Url = baseUrl + "/api/test/error-endpoint"; + } + else + { + httpTask.Url = "http://mocklab:5000/api/test/error-endpoint"; + } var body = new { source = "error-boundary-test", diff --git a/core/Workflows/error-boundary/src/IgnoreErrorMapping.csx b/core/Workflows/error-boundary/src/IgnoreErrorMapping.csx index e0afa8b..9a09979 100644 --- a/core/Workflows/error-boundary/src/IgnoreErrorMapping.csx +++ b/core/Workflows/error-boundary/src/IgnoreErrorMapping.csx @@ -16,9 +16,10 @@ public class IgnoreErrorMapping : ScriptBase, IMapping dynamic result = new ExpandoObject(); if (HasProperty(data, "testId")) result.testId = data.testId; if (HasProperty(data, "errorTestStarted")) result.errorTestStarted = data.errorTestStarted; - if (HasProperty(data, "httpErrorHandled")) result.httpErrorHandled = data.httpErrorHandled; + if (HasProperty(data, "startedAt")) result.startedAt = data.startedAt; + if (HasProperty(data, "retryHandled")) result.retryHandled = data.retryHandled; result.errorIgnored = true; - LogInformation("IgnoreErrorMapping: continuation after ignore policy"); + LogInformation("IgnoreErrorMapping: error was ignored, continuation confirmed"); return Task.FromResult(new ScriptResponse { Data = result }); } } diff --git a/core/Workflows/error-boundary/src/LogOnlyMapping.csx b/core/Workflows/error-boundary/src/LogOnlyMapping.csx index 4e54c8b..8df6223 100644 --- a/core/Workflows/error-boundary/src/LogOnlyMapping.csx +++ b/core/Workflows/error-boundary/src/LogOnlyMapping.csx @@ -16,12 +16,11 @@ public class LogOnlyMapping : ScriptBase, IMapping dynamic result = new ExpandoObject(); if (HasProperty(data, "testId")) result.testId = data.testId; if (HasProperty(data, "errorTestStarted")) result.errorTestStarted = data.errorTestStarted; - if (HasProperty(data, "httpErrorHandled")) result.httpErrorHandled = data.httpErrorHandled; + if (HasProperty(data, "startedAt")) result.startedAt = data.startedAt; + if (HasProperty(data, "retryHandled")) result.retryHandled = data.retryHandled; if (HasProperty(data, "errorIgnored")) result.errorIgnored = data.errorIgnored; - if (HasProperty(data, "rollbackExecuted")) result.rollbackExecuted = data.rollbackExecuted; - if (HasProperty(data, "rollbackAt")) result.rollbackAt = data.rollbackAt; - result.logOnlyExecuted = true; - LogInformation("LogOnlyMapping: log-only path executed"); + result.logHandled = true; + LogInformation("LogOnlyMapping: log-only action confirmed, flow continues"); return Task.FromResult(new ScriptResponse { Data = result }); } } diff --git a/core/Workflows/error-boundary/src/PriorityConfirmMapping.csx b/core/Workflows/error-boundary/src/PriorityConfirmMapping.csx new file mode 100644 index 0000000..7e4c9f1 --- /dev/null +++ b/core/Workflows/error-boundary/src/PriorityConfirmMapping.csx @@ -0,0 +1,27 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; + +public class PriorityConfirmMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public Task OutputHandler(ScriptContext context) + { + var data = context.Instance.Data; + dynamic result = new ExpandoObject(); + if (HasProperty(data, "testId")) result.testId = data.testId; + if (HasProperty(data, "errorTestStarted")) result.errorTestStarted = data.errorTestStarted; + if (HasProperty(data, "startedAt")) result.startedAt = data.startedAt; + if (HasProperty(data, "retryHandled")) result.retryHandled = data.retryHandled; + if (HasProperty(data, "errorIgnored")) result.errorIgnored = data.errorIgnored; + if (HasProperty(data, "logHandled")) result.logHandled = data.logHandled; + result.priorityRuleApplied = true; + LogInformation("PriorityConfirmMapping: priority-based rule selection confirmed"); + return Task.FromResult(new ScriptResponse { Data = result }); + } +} diff --git a/core/Workflows/error-boundary/src/RetryConfirmMapping.csx b/core/Workflows/error-boundary/src/RetryConfirmMapping.csx new file mode 100644 index 0000000..a3e01b1 --- /dev/null +++ b/core/Workflows/error-boundary/src/RetryConfirmMapping.csx @@ -0,0 +1,24 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; + +public class RetryConfirmMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public Task OutputHandler(ScriptContext context) + { + var data = context.Instance.Data; + dynamic result = new ExpandoObject(); + if (HasProperty(data, "testId")) result.testId = data.testId; + if (HasProperty(data, "errorTestStarted")) result.errorTestStarted = data.errorTestStarted; + if (HasProperty(data, "startedAt")) result.startedAt = data.startedAt; + result.retryHandled = true; + LogInformation("RetryConfirmMapping: retry boundary exhausted + ignore fallback confirmed"); + return Task.FromResult(new ScriptResponse { Data = result }); + } +} diff --git a/core/Workflows/error-boundary/src/StateLevelConfirmMapping.csx b/core/Workflows/error-boundary/src/StateLevelConfirmMapping.csx new file mode 100644 index 0000000..a2336f9 --- /dev/null +++ b/core/Workflows/error-boundary/src/StateLevelConfirmMapping.csx @@ -0,0 +1,28 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; + +public class StateLevelConfirmMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public Task OutputHandler(ScriptContext context) + { + var data = context.Instance.Data; + dynamic result = new ExpandoObject(); + if (HasProperty(data, "testId")) result.testId = data.testId; + if (HasProperty(data, "errorTestStarted")) result.errorTestStarted = data.errorTestStarted; + if (HasProperty(data, "startedAt")) result.startedAt = data.startedAt; + if (HasProperty(data, "retryHandled")) result.retryHandled = data.retryHandled; + if (HasProperty(data, "errorIgnored")) result.errorIgnored = data.errorIgnored; + if (HasProperty(data, "logHandled")) result.logHandled = data.logHandled; + if (HasProperty(data, "priorityRuleApplied")) result.priorityRuleApplied = data.priorityRuleApplied; + result.stateLevelHandled = true; + LogInformation("StateLevelConfirmMapping: state-level error boundary confirmed"); + return Task.FromResult(new ScriptResponse { Data = result }); + } +} diff --git a/core/Workflows/error-boundary/src/ThrowErrorMapping.csx b/core/Workflows/error-boundary/src/ThrowErrorMapping.csx index 1f49421..98f3c63 100644 --- a/core/Workflows/error-boundary/src/ThrowErrorMapping.csx +++ b/core/Workflows/error-boundary/src/ThrowErrorMapping.csx @@ -11,6 +11,6 @@ public class ThrowErrorMapping : ScriptBase, IMapping public Task OutputHandler(ScriptContext context) { - throw new Exception("Intentional test error for error boundary"); + throw new InvalidOperationException("Intentional test error for error boundary"); } } diff --git a/core/Workflows/flow-types/core-flow-test.json b/core/Workflows/flow-types/core-flow-test.json index b1bf636..4fc52c3 100644 --- a/core/Workflows/flow-types/core-flow-test.json +++ b/core/Workflows/flow-types/core-flow-test.json @@ -4,11 +4,7 @@ "flowVersion": "1.0.0", "domain": "core", "version": "1.0.0", - "tags": [ - "integration-test", - "flow-types", - "core" - ], + "tags": ["integration-test", "flow-types", "core"], "attributes": { "type": "C", "timeout": null, @@ -119,4 +115,4 @@ } ] } -} \ No newline at end of file +} diff --git a/docs/integration-test-documentation.md b/docs/integration-test-documentation.md index 07ffa6c..d6771fc 100644 --- a/docs/integration-test-documentation.md +++ b/docs/integration-test-documentation.md @@ -11,7 +11,7 @@ Bu dokuman, `core` projesindeki tum integration test workflow'larini, icerdikler | 1 | Lifecycle & Transitions | `lifecycle-transitions-test-workflow` | State tipleri, transition tipleri, on-entries/exits, timer, cancel, **exit** (`attributes.exit`), **schedule iptal / timer reschedule**, **queryRoles** ve **transition `roles` (yalnizca state fonksiyonu listeleme filtresi)** | | 2 | SubFlow & SubProcess | `subflow-orchestration-parent` + child + grandchild | Parent-child-grandchild zinciri, parent shared transitions, **child shared transition**, **cancel cascade** (child/grandchild **cancelled** final state'leri), **effective state**, **updateData (SubFlow)**, **subFlow.overrides** (timeout, states.queryRoles, transitions.roles) | | 3 | Task Execution | `task-execution-test-workflow` + `task-target-workflow` + `extended-tasks-test-workflow` | HTTP/Script/StartFlow/GetInstanceData/Notification/TriggerTransition/SubProcess/GetInstances; Dapr HTTP/Service/PubSub (`extended-tasks-test-workflow`); Mocklab + Dapr YAML | -| 4 | Error Boundary | `error-boundary-test-workflow` | Retry, Ignore, **Rollback**, **Log**, **Notify** (action:4) aksiyonlari, retry policy, priority, **timeoutPolicy** (onTimeout), **errorHandlerRule** (errorTypes/errorCodes) | +| 4 | Error Boundary | `error-boundary-test-workflow` | Task/state/global errorBoundary; Retry/Ignore/Log/Rollback/Notify/Abort; retry policy; priority; errorTypes/errorCodes. **`errorBoundary.onTimeout` bu flow'da test edilmez** (bkz. dokuman sonu: Test Edilmeyen Ozellikler) | | 5 | View, Function & Extension (Comprehensive) | `view-function-extension-test-workflow` | 6 view tipi (JSON/HTML/MD/DeepLink/HTTP/URN), 6 display modu, 4 extension tipi (4 type × 3 scope), 3 function scope (I/F/D), wizard state, implicit global extension, `?extensions=` sorgusu, `functions/view` icerik dogrulamasi | | 6 | Schema Validation | `schema-validation-test-workflow` | Master + start + transition + cancel + exit + updateData schema, field roles, SubFlow updateData | | 7 | Instance Management | `instance-management-test-workflow` | Filtering, pagination, sorting, timeout, idempotent start, **subType 4/5/6** (suspended/busy/human) | @@ -462,9 +462,13 @@ extended-tasks-test-workflow (F) **C# integration tests:** `tests/Core/Tests/task-execution-test-workflow/` (`TaskExecutionTestWorkflowTests`, `TaskExecutionInstanceDataAssertions`) — B1/B2/B3 happy paths, instance data contract on the main workflow, and extended Dapr `taskResults` flags. +--- + +## Grup 4: Error Boundary + **Workflow:** `error-boundary-test-workflow` (type: F) -**Amac:** Hata yonetimi mekanizmasini (error boundary) task, state ve workflow seviyelerinde test eder; **Rollback (action:2)**, **Log (action:5)** ve **Notify (action:4)** aksiyonlari ile zincirlenmis senaryoyu kapsar. Ayrica **timeoutPolicy** (onTimeout) ve **gelismis errorHandlerRule** (errorTypes/errorCodes filtreleme) ozelliklerini icerir. +**Amac:** Hata yonetimi mekanizmasini (error boundary) task, state ve workflow seviyelerinde test eder; **Rollback (action:2)**, **Log (action:5)**, **Notify (action:4)**, **Abort (action:0)** ve ilgili aksiyonlari priority / errorTypes / errorCodes ile kapsar. **`errorBoundary.onTimeout` workflow taniminda yer almaz ve integration test ile dogrulanmaz** — runtime dokumantasyonunda da henuz implemente edilmedigi belirtilmistir; bkz. dokuman sonu. ### Agac Yapisi @@ -476,8 +480,8 @@ error-boundary-test-workflow (F) │ └── error-script-task + InitErrorTestMapping.csx │ ├── [workflow-level errorBoundary] -│ ├── onError: action:0 (Abort), priority:100 -│ └── onTimeout: action:3 (Ignore) +│ └── onError: action:0 (Abort), wildcard fallback (transition yok — schema kisiti) +│ (onTimeout tanimi YOK — test kapsami disi, bkz. dokuman sonu) │ ├── retry-test-state (Initial, stateType:1) │ ├── onEntries: @@ -546,10 +550,8 @@ error-boundary-test-workflow (F) | **Rollback action (action:2)** | State errorBoundary rollback | rollback-test-state | | **Log action (action:5)** | Hata loglama; siradaki task ile devam | log-test-state | | **Notify action (action:4)** | Hata bildirim ve transition tetikleme | notify-test-state errorBoundary | -| Abort action (action:0) | En son calisacak global yakalama | workflow errorBoundary | -| **timeoutPolicy (onTimeout)** | Timeout durumunda action:3 (Ignore) | workflow errorBoundary.onTimeout | -| **errorHandlerRule (errorTypes)** | Hata tipine gore filtreleme | ignore-test-state: System.InvalidOperationException | -| **errorHandlerRule (errorCodes)** | Hata koduna gore filtreleme | ignore-test-state: errorCodes:["*"] | +| Abort action (action:0) | Global wildcard fallback (transition kullanilmaz; instance Faulted) | workflow errorBoundary | +| **errorTypes / errorCodes** | Kural bazli eslestirme | priority-rules-state ve diger boundary kurallari | | Priority siralama | Dusuk priority once calisir | task:10, state:10, workflow:100 | | Hata sonrasi devam | Ignore sonrasi siradaki task calisir | IgnoreErrorMapping (order:2) | | Rollback sonrasi zincir | rollback-test-state tamamlaninca log-test-state | auto-to-log-from-rollback | @@ -570,7 +572,7 @@ error-boundary-test-workflow (F) | CSX | **NotifyMapping** | Workflows/error-boundary/src/NotifyMapping.csx | | CSX | AlwaysTrueRule | Workflows/error-boundary/src/AlwaysTrueRule.csx | -**Workflow tag'leri (ozet):** `rollback`, `log-action`, `notify`, `notify-action`, `timeout-policy`, `error-types`, `error-codes` dahil; tam liste `error-boundary-test-workflow.json` icindedir. +**Workflow tag'leri (ozet):** `rollback`, `log-action`, `notify`, `abort`, `priority`, `error-types`, `error-codes` dahil; tam liste `error-boundary-test-workflow.json` icindedir. --- @@ -1425,8 +1427,7 @@ Her grubun hangi vNext ozelliklerini test ettigini gosteren matris. **Gn** sutun | **Rollback (action:2)** | | | | X | | | | | | | | **Log action (action:5, state)** | | | | X | | | | | | | | **Notify action (action:4)** | | | | X | | | | | | | -| **timeoutPolicy (onTimeout)** | | | | X | | | | | | | -| **errorHandlerRule (errorTypes/errorCodes)** | | | | X | | | | | | | +| **errorBoundary errorTypes / errorCodes** | | | | X | | | | | | | | View (6 tip: JSON/HTML/MD/DeepLink/HTTP/URN) | | | | | X | | | | | | | Display modlari (6: full-page/popup/bottom-sheet/top-sheet/drawer/inline) | | | | | X | | | | | | | `functions/view` icerik dogrulamasi (type, display, content) | | | | | X | | | | | | @@ -1496,9 +1497,21 @@ Bu bolum, runtime tarafindan desteklenen ancak `vnext-example` integration test - **Iliskili ama farkli ozellik:** `instance-management-test-workflow` icindeki `human-state` (subType 6) Human **Task** tipi degil, **state alt tipi**dir. Bu state, runtime'in `subType` enum'unu test eder ve Human Task tip 5'in kaldirilma kararindan **etkilenmez**. - **Eklenmek istenirse:** Runtime'da Human Task ozelligi kalici olarak destekleneceginin teyidi alindiginda; ayri bir `human-task-test-workflow` ve `human-task` (tip 5) JSON tanimi olusturulup Mocklab veya gercek bir human-task hub uctan uca testle entegre edilmelidir. Su an icin **eklenmemelidir**. +### 3. Error Boundary — `onTimeout` (timeout yapisi) + +- **Konum:** `error-boundary-test-workflow.json` — **`errorBoundary.onTimeout` alani tanimlanmaz**; integration test workflow'u bu yapilandirmayi **dogrulamaz**. +- **Runtime dokumantasyonu:** `vnext-runtime/doc/tr/flow/error-boundary.md` dosyasinda `onTimeout` ozelliginin semada bulundugu ancak **henuz implemente edilmedigi** belirtilmistir. +- **Iliski:** Workflow veya instance duzeyinde **zaman asimi** (`attributes.timeout` vb.) **Grup 7 (Instance Management)** ve baska workflow'larda ayri kavram olarak test edilebilir; bu, **error boundary `onTimeout`** ile ayni sey degildir. +- **Eklenmek istenirse:** Runtime'da `errorBoundary.onTimeout` destegi netlestikten sonra `error-boundary-test-workflow` (veya ayri bir workflow) ile dedicated senaryo eklenmeli; simdilik bu dokuman ve ozellik matrisi bu ozelligi **Grup 4 kapsaminda test edilmiyor** olarak isaretler. + ### Ozet Tablo | Ozellik | Tip / Konum | Durum | Gerekce | |---------|-------------|-------|---------| | Dapr Binding Task | Task tipi 2, `extended-tasks-test-workflow` | **Kaldirildi (state + task + mapping silindi)** | Runtime kapsamindaki kararsizlik; mocklab/Dapr binding entegrasyonu uctan uca dogrulanamiyor | | Human Task | Task tipi 5, daha once `task-execution-test-workflow` zincirinde | **Kaldirildi / yeni testlere eklenmez** | Runtime tarafindan kaldirilacak gecici ozellik; sadece state subType 6 (Human) korunur | +| **Error Boundary `onTimeout`** | `attributes.errorBoundary.onTimeout`, Grup 4 workflow | **Test edilmiyor** | Runtime'da henuz implemente edilmemis (semada var); `error-boundary-test-workflow` icinde tanim yok — bkz. **§3** yukarida | + +--- + +**Not (dokuman sonu):** Grup 4 `error-boundary-test-workflow` **error boundary `onTimeout`** yapisini icermez ve integration testleri bunu dogrulamaz; ayrinti **Test Edilmeyen Ozellikler → §3** ve Ozet Tablo'daki ilgili satirda ozetlenmistir. diff --git a/tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryInstanceDataAssertions.cs b/tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryInstanceDataAssertions.cs new file mode 100644 index 0000000..f92c13a --- /dev/null +++ b/tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryInstanceDataAssertions.cs @@ -0,0 +1,67 @@ +using System.Text.Json; +using Core.IntegrationTests.Helpers; +using Xunit; + +namespace Core.IntegrationTests.Tests.ErrorBoundaryTestWorkflow; + +/// +/// Instance attributes contract for error-boundary-test-workflow. +/// Field names align with core/Workflows/error-boundary/src/*.csx mappings. +/// +internal static class ErrorBoundaryInstanceDataAssertions +{ + private const string ContractHint = + "(error-boundary-test-workflow CSX contract)"; + + public static void AssertInitMappingExecuted(JsonElement attributes) + { + JsonElementAssertions.AssertPropertyTrue( + attributes, "errorTestStarted", + $"errorTestStarted should be true {ContractHint}" + ); + JsonElementAssertions.AssertPropertyNonEmptyString( + attributes, "testId", + $"testId should be non-empty {ContractHint}" + ); + } + + public static void AssertRetryHandled(JsonElement attributes) + { + JsonElementAssertions.AssertPropertyTrue( + attributes, "retryHandled", + $"retryHandled should be true after retry+ignore fallback {ContractHint}" + ); + } + + public static void AssertIgnoreExecuted(JsonElement attributes) + { + JsonElementAssertions.AssertPropertyTrue( + attributes, "errorIgnored", + $"errorIgnored should be true after ignore boundary {ContractHint}" + ); + } + + public static void AssertLogHandled(JsonElement attributes) + { + JsonElementAssertions.AssertPropertyTrue( + attributes, "logHandled", + $"logHandled should be true after log boundary {ContractHint}" + ); + } + + public static void AssertPriorityRuleApplied(JsonElement attributes) + { + JsonElementAssertions.AssertPropertyTrue( + attributes, "priorityRuleApplied", + $"priorityRuleApplied should be true (specific rule matched before fallback) {ContractHint}" + ); + } + + public static void AssertStateLevelHandled(JsonElement attributes) + { + JsonElementAssertions.AssertPropertyTrue( + attributes, "stateLevelHandled", + $"stateLevelHandled should be true (state-level boundary caught error) {ContractHint}" + ); + } +} diff --git a/tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryTestWorkflowTests.cs b/tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryTestWorkflowTests.cs new file mode 100644 index 0000000..8e8e539 --- /dev/null +++ b/tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryTestWorkflowTests.cs @@ -0,0 +1,136 @@ +using System.Text.Json; +using Core.IntegrationTests.Helpers; +using Core.IntegrationTests.Infrastructure; +using Xunit; + +namespace Core.IntegrationTests.Tests.ErrorBoundaryTestWorkflow; + +/// +/// Integration tests for error-boundary-test-workflow. +/// Tests error boundary actions at task-level, state-level, and global-level. +/// Actions tested: Retry (1), Rollback (2), Ignore (3), Notify (4), Log (5), Abort (0). +/// Flow: init → task-retry → task-ignore → task-log → priority-rules → state-level-test +/// → notify-test → [notify-redirect] → rollback-test → [rollback-redirect] +/// → global-abort-test → (Global Abort → Faulted) +/// +public class ErrorBoundaryTestWorkflowTests : IntegrationTestBase +{ + private const string WorkflowKey = "error-boundary-test-workflow"; + + private static readonly TimeSpan StateTimeout = TimeSpan.FromMinutes(3); + + private readonly WorkflowInstanceTestHelper _workflow; + + public ErrorBoundaryTestWorkflowTests(VNextTestEnvironment environment) + : base(environment) + { + _workflow = new WorkflowInstanceTestHelper(Api, WorkflowKey); + } + + [Fact] + public async Task TaskLevel_RetryAction_RetriesExhaustedThenIgnoreFallback() + { + var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-retry"); + var attrs = await _workflow.GetAttributesAsync(instanceId); + ErrorBoundaryInstanceDataAssertions.AssertInitMappingExecuted(attrs); + ErrorBoundaryInstanceDataAssertions.AssertRetryHandled(attrs); + } + + [Fact] + public async Task TaskLevel_IgnoreAction_ErrorIgnoredNextTaskRuns() + { + var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-ignore"); + var attrs = await _workflow.GetAttributesAsync(instanceId); + ErrorBoundaryInstanceDataAssertions.AssertInitMappingExecuted(attrs); + ErrorBoundaryInstanceDataAssertions.AssertIgnoreExecuted(attrs); + } + + [Fact] + public async Task TaskLevel_LogAction_ErrorLoggedNextTaskRuns() + { + var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-log"); + var attrs = await _workflow.GetAttributesAsync(instanceId); + ErrorBoundaryInstanceDataAssertions.AssertInitMappingExecuted(attrs); + ErrorBoundaryInstanceDataAssertions.AssertLogHandled(attrs); + } + + [Fact] + public async Task TaskLevel_PriorityRules_SpecificRuleMatchesBeforeFallback() + { + var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-priority"); + var attrs = await _workflow.GetAttributesAsync(instanceId); + ErrorBoundaryInstanceDataAssertions.AssertInitMappingExecuted(attrs); + ErrorBoundaryInstanceDataAssertions.AssertPriorityRuleApplied(attrs); + } + + [Fact] + public async Task StateLevel_IgnoreAction_ErrorHandledByStateBoundary() + { + var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-state-level"); + var attrs = await _workflow.GetAttributesAsync(instanceId); + ErrorBoundaryInstanceDataAssertions.AssertStateLevelHandled(attrs); + } + + [Fact] + public async Task TaskLevel_NotifyAction_TransitionTriggered() + { + var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-notify"); + Assert.True( + state == "rollback-test-state" || state == "global-abort-test-state" || status == "F", + $"Notify action should redirect flow. Actual state='{state}', status='{status}'" + ); + } + + [Fact] + public async Task TaskLevel_RollbackAction_TransitionTriggered() + { + var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-rollback"); + Assert.True( + state == "global-abort-test-state" || status == "F", + $"Rollback action should redirect flow. Actual state='{state}', status='{status}'" + ); + } + + [Fact] + public async Task GlobalLevel_AbortAction_InstanceFaulted() + { + var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-global-abort"); + Assert.Equal("F", status); + Assert.Equal("global-abort-test-state", state); + } + + private async Task<(string instanceId, string? status, string state)> StartAndWaitForFinalAsync(string prefix) + { + var key = WorkflowInstanceTestHelper.UniqueInstanceKey(prefix); + var instanceId = await _workflow.StartInstanceIdAsync( + new + { + key, + tags = new[] { "integration-test", "error-boundary", prefix }, + attributes = new { }, + } + ); + + var deadline = DateTime.UtcNow + StateTimeout; + string currentState = ""; + string? status = null; + + while (DateTime.UtcNow < deadline) + { + var stateBody = await _workflow.GetStateFunctionBodyAsync(instanceId, headers: null); + currentState = StateFunctionJson.ExtractStateName(stateBody); + status = StateFunctionJson.ExtractStatus(stateBody); + + if (status == "F" || status == "C") + return (instanceId, status, currentState); + + if (currentState == "completed-state") + return (instanceId, status, currentState); + + await Task.Delay(1000); + } + + Assert.Fail($"Timeout waiting for final state. Last state='{currentState}', status='{status}'"); + return (instanceId, status, currentState); + } +} From e092949b093fa1f1a5a80c992b03586e679021f4 Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Fri, 8 May 2026 17:08:47 +0300 Subject: [PATCH 22/24] error boundary workflow has been published to runtime but there is errors on tests, probably caused by wrong workflow definition. --- .../postman-error-boundary.json | 115 ++++++++++++++---- core.link.json | 2 +- .../error-boundary-test-workflow.json | 10 +- .../error-boundary/src/ErrorHttpMapping.csx | 53 -------- .../error-boundary/src/NotifyMapping.csx | 39 ------ .../src/RetryConfirmMapping.csx | 18 ++- .../error-boundary/src/RetryThrowMapping.csx | 58 +++++++++ .../error-boundary/src/RollbackMapping.csx | 33 ----- core/Workflows/flow-types/core-flow-test.json | 8 +- docs/integration-test-documentation.md | 113 +++++------------ .../ErrorBoundaryInstanceDataAssertions.cs | 82 ++++++++++++- .../ErrorBoundaryTestWorkflowTests.cs | 38 +++--- 12 files changed, 313 insertions(+), 256 deletions(-) delete mode 100644 core/Workflows/error-boundary/src/ErrorHttpMapping.csx delete mode 100644 core/Workflows/error-boundary/src/NotifyMapping.csx create mode 100644 core/Workflows/error-boundary/src/RetryThrowMapping.csx delete mode 100644 core/Workflows/error-boundary/src/RollbackMapping.csx diff --git a/api-tests/error-boundary/postman-error-boundary.json b/api-tests/error-boundary/postman-error-boundary.json index 4f97441..f0efe56 100644 --- a/api-tests/error-boundary/postman-error-boundary.json +++ b/api-tests/error-boundary/postman-error-boundary.json @@ -2,7 +2,7 @@ "info": { "_postman_id": "b383d938-352a-4425-a364-3e3f27ce9306", "name": "vNext Test - Error Boundary", - "description": "Auto-generated from error-boundary-test-workflow.http.\nvNext workflow integration test collection for error-boundary.", + "description": "error-boundary-test-workflow (core, flow error-boundary-test-workflow / sys-flows).\n\n**Akış (otomatik geçişler):**\ninit-state → task-retry-state → task-ignore-state → task-log-state → priority-rules-state → state-level-test-state → notify-test-state → [notify-redirect] → rollback-test-state → [rollback-redirect] → global-abort-test-state → **Fault (status F)** — workflow geneli errorBoundary Abort (0).\n\n**Task-level örnekleri:** Retry+Ignore, Ignore, Log-only, öncelikli Ignore / Abort fallback, Notify (transition), Rollback (transition). **State-level:** state errorBoundary + Ignore.\n**Global:** global-abort-test-state içindeki hata → Abort.\n\nTüm görevler **error-script-task** (script); bu koleksiyonda **HTTP/Mockoon/Mocklab gerekmez**. `task-retry-state` retryPolicy PT1S×backoff içerir — `sync=true` ile başlatırsanız yanıt birkaç saniye sürebilir.\n\n**C# testleri:** `tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryTestWorkflowTests.cs` — milestone için GET instance `attributes` doğrulaması; tam akış sonunda fault beklenir.\n\n**functions/data / attributes ile uyumlu bayraklar (CSX):** errorTestStarted, testId; retryHandled; errorIgnored; logHandled; priorityRuleApplied; stateLevelHandled; notify-test sonrası notifyExecuted, notifyAt; rollback-test sonrası rollbackExecuted, rollbackAt.", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "variable": [ @@ -27,14 +27,14 @@ "type": "string" }, { - "key": "mocklabBaseUrl", - "value": "http://localhost:3002", + "key": "errorBoundaryInstanceId", + "value": "", "type": "string" } ], "item": [ { - "name": "Adim 1: Workflow baslat (sync; HTTP task retries may add delay)", + "name": "1) Instance başlat (sync — retry gecikmesi olabilir)", "request": { "method": "POST", "header": [ @@ -46,9 +46,7 @@ ], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true", - "host": [ - "{{baseUrl}}" - ], + "host": ["{{baseUrl}}"], "path": [ "api", "v{{apiVersion}}", @@ -67,13 +65,14 @@ }, "body": { "mode": "raw", - "raw": "{\n \"key\": \"error-boundary-test-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"error-boundary\"],\n \"attributes\": {}\n}", + "raw": "{\n \"key\": \"error-boundary-postman-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"error-boundary\", \"postman\"],\n \"attributes\": {}\n}", "options": { "raw": { "language": "json" } } - } + }, + "description": "`start-error-test` → **init-state**. İlk görev InitErrorTestMapping (errorTestStarted, testId). Sonraki state’ler tamamen otomatik." }, "response": [], "event": [ @@ -83,9 +82,12 @@ "type": "text/javascript", "exec": [ "var jsonData = pm.response.json();", + "pm.test(\"Start 200 ve id var\", function () {", + " pm.response.to.have.status(200);", + " pm.expect(jsonData).to.have.property(\"id\");", + "});", "if (jsonData && jsonData.id) {", " pm.collectionVariables.set(\"errorBoundaryInstanceId\", jsonData.id);", - " console.log(\"errorBoundaryInstanceId set to: \" + jsonData.id);", "}" ] } @@ -93,15 +95,13 @@ ] }, { - "name": "Adim 2: Durum (notify-test-state uzerinden completed-state beklenir; global abort veya basarisizlikta error-final)", + "name": "2) State (long-poll / anlık)", "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{errorBoundaryInstanceId}}/functions/state", - "host": [ - "{{baseUrl}}" - ], + "host": ["{{baseUrl}}"], "path": [ "api", "v{{apiVersion}}", @@ -113,20 +113,19 @@ "functions", "state" ] - } + }, + "description": "Tipik sonuç: **state** ≈ `global-abort-test-state`, **status** = `F`. Ara durumlar çok hızlı geçebilir (task-retry-state … notify-test-state)." }, "response": [] }, { - "name": "Adim 3: Instance data (errorTestStarted, httpErrorHandled, errorIgnored, rollbackExecuted, rollbackAt, logOnlyExecuted, notifyExecuted, notifyAt)", + "name": "3) Instance data (functions/data)", "request": { "method": "GET", "header": [], "url": { "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{errorBoundaryInstanceId}}/functions/data", - "host": [ - "{{baseUrl}}" - ], + "host": ["{{baseUrl}}"], "path": [ "api", "v{{apiVersion}}", @@ -138,9 +137,83 @@ "functions", "data" ] - } + }, + "description": "Response gövdesinde instance verisi (çoğu kurulumda `data` alt nesnesi). Örnek alanlar: errorTestStarted, testId, retryHandled, errorIgnored, logHandled, priorityRuleApplied, stateLevelHandled; notifyExecuted, notifyAt; rollbackExecuted, rollbackAt. Fault sonrası tam görünürlük ortama göre değişebilir — ara milestone için state/data tekrar çağrılabilir." + }, + "response": [] + }, + { + "name": "4) Instance envelope (GET instance — opsiyonel)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{errorBoundaryInstanceId}}", + "host": ["{{baseUrl}}"], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "{{errorBoundaryInstanceId}}" + ] + }, + "description": "metadata.currentState / metadata.status ve `attributes` ile instance özeti (runtime sürümüne bağlı)." }, "response": [] + }, + { + "name": "1b) Instance başlat (async — start hemen döner)", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start", + "host": ["{{baseUrl}}"], + "path": [ + "api", + "v{{apiVersion}}", + "{{domain}}", + "workflows", + "{{workflow}}", + "instances", + "start" + ] + }, + "body": { + "mode": "raw", + "raw": "{\n \"key\": \"error-boundary-async-{{$timestamp}}\",\n \"tags\": [\"integration-test\", \"error-boundary\", \"postman-async\"],\n \"attributes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "description": "`sync` query yok; akış arka planda ilerler. Ardından **2) State** ile poll edin." + }, + "response": [], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = pm.response.json();", + "if (jsonData && jsonData.id) {", + " pm.collectionVariables.set(\"errorBoundaryInstanceId\", jsonData.id);", + "}" + ] + } + } + ] } ] -} \ No newline at end of file +} diff --git a/core.link.json b/core.link.json index 334907e..89011eb 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-05-08T10:54:41.263Z" + "importedAt": "2026-05-08T13:15:19.217Z" } \ No newline at end of file diff --git a/core/Workflows/error-boundary/error-boundary-test-workflow.json b/core/Workflows/error-boundary/error-boundary-test-workflow.json index 8bdbb3f..bf3e310 100644 --- a/core/Workflows/error-boundary/error-boundary-test-workflow.json +++ b/core/Workflows/error-boundary/error-boundary-test-workflow.json @@ -120,8 +120,8 @@ "flow": "sys-tasks" }, "mapping": { - "location": "./src/ThrowErrorMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIFRocm93RXJyb3JNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0aHJvdyBuZXcgSW52YWxpZE9wZXJhdGlvbkV4Y2VwdGlvbigiSW50ZW50aW9uYWwgdGVzdCBlcnJvciBmb3IgZXJyb3IgYm91bmRhcnkiKTsKICAgIH0KfQo=" + "location": "./src/RetryThrowMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gU2FtZSBpbnRlbnRpb25hbCBmYWlsdXJlIGFzIFRocm93RXJyb3JNYXBwaW5nLCBidXQgaW5jcmVtZW50cyByZXRyeVRocm93QXR0ZW1wdENvdW50Ci8vLyBpbiBJbnB1dEhhbmRsZXIgc28gZWFjaCByZXRyeSBhdHRlbXB0IGlzIG9ic2VydmFibGUgaW4gaW5zdGFuY2UgZGF0YSAoYmVmb3JlIE91dHB1dEhhbmRsZXIgdGhyb3dzKS4KLy8vIEFsc28gcmVjb3JkcyBJU08gODYwMSBVVEMgc3RhbXBzIHBlciBhdHRlbXB0IGZvciBiYWNrb2ZmIHRpbWluZyBhc3NlcnRpb25zLgovLy8gPC9zdW1tYXJ5PgpwdWJsaWMgY2xhc3MgUmV0cnlUaHJvd01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwoKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKQogICAgICAgICAgICByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJlcnJvclRlc3RTdGFydGVkIikpCiAgICAgICAgICAgIHJlc3VsdC5lcnJvclRlc3RTdGFydGVkID0gZGF0YS5lcnJvclRlc3RTdGFydGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpCiAgICAgICAgICAgIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKCiAgICAgICAgdmFyIHByaW9yID0gMDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInJldHJ5VGhyb3dBdHRlbXB0Q291bnQiKSkKICAgICAgICB7CiAgICAgICAgICAgIHRyeQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBwcmlvciA9IENvbnZlcnQuVG9JbnQzMihkYXRhLnJldHJ5VGhyb3dBdHRlbXB0Q291bnQpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGNhdGNoCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIHByaW9yID0gMDsKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgdmFyIG5leHQgPSBwcmlvciArIDE7CiAgICAgICAgcmVzdWx0LnJldHJ5VGhyb3dBdHRlbXB0Q291bnQgPSBuZXh0OwoKICAgICAgICB2YXIgc3RhbXAgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKICAgICAgICBpZiAobmV4dCA9PSAxKQogICAgICAgICAgICByZXN1bHQucmV0cnlBdHRlbXB0MVV0YyA9IHN0YW1wOwogICAgICAgIGVsc2UgaWYgKG5leHQgPT0gMikKICAgICAgICAgICAgcmVzdWx0LnJldHJ5QXR0ZW1wdDJVdGMgPSBzdGFtcDsKICAgICAgICBlbHNlIGlmIChuZXh0ID09IDMpCiAgICAgICAgICAgIHJlc3VsdC5yZXRyeUF0dGVtcHQzVXRjID0gc3RhbXA7CgogICAgICAgIExvZ0luZm9ybWF0aW9uKCQiUmV0cnlUaHJvd01hcHBpbmcgSW5wdXRIYW5kbGVyIGF0dGVtcHQge25leHR9Iik7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0aHJvdyBuZXcgSW52YWxpZE9wZXJhdGlvbkV4Y2VwdGlvbigiSW50ZW50aW9uYWwgdGVzdCBlcnJvciBmb3IgZXJyb3IgYm91bmRhcnkiKTsKICAgIH0KfQo=" }, "errorBoundary": { "onError": [ @@ -157,7 +157,7 @@ }, "mapping": { "location": "./src/RetryConfirmMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBSZXRyeUNvbmZpcm1NYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiZXJyb3JUZXN0U3RhcnRlZCIpKSByZXN1bHQuZXJyb3JUZXN0U3RhcnRlZCA9IGRhdGEuZXJyb3JUZXN0U3RhcnRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInN0YXJ0ZWRBdCIpKSByZXN1bHQuc3RhcnRlZEF0ID0gZGF0YS5zdGFydGVkQXQ7CiAgICAgICAgcmVzdWx0LnJldHJ5SGFuZGxlZCA9IHRydWU7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlJldHJ5Q29uZmlybU1hcHBpbmc6IHJldHJ5IGJvdW5kYXJ5IGV4aGF1c3RlZCArIGlnbm9yZSBmYWxsYmFjayBjb25maXJtZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFJldHJ5Q29uZmlybU1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKQogICAgICAgICAgICByZXN1bHQudGVzdElkID0gZGF0YS50ZXN0SWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJlcnJvclRlc3RTdGFydGVkIikpCiAgICAgICAgICAgIHJlc3VsdC5lcnJvclRlc3RTdGFydGVkID0gZGF0YS5lcnJvclRlc3RTdGFydGVkOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAic3RhcnRlZEF0IikpCiAgICAgICAgICAgIHJlc3VsdC5zdGFydGVkQXQgPSBkYXRhLnN0YXJ0ZWRBdDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInJldHJ5VGhyb3dBdHRlbXB0Q291bnQiKSkKICAgICAgICAgICAgcmVzdWx0LnJldHJ5VGhyb3dBdHRlbXB0Q291bnQgPSBkYXRhLnJldHJ5VGhyb3dBdHRlbXB0Q291bnQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJyZXRyeUF0dGVtcHQxVXRjIikpCiAgICAgICAgICAgIHJlc3VsdC5yZXRyeUF0dGVtcHQxVXRjID0gZGF0YS5yZXRyeUF0dGVtcHQxVXRjOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAicmV0cnlBdHRlbXB0MlV0YyIpKQogICAgICAgICAgICByZXN1bHQucmV0cnlBdHRlbXB0MlV0YyA9IGRhdGEucmV0cnlBdHRlbXB0MlV0YzsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInJldHJ5QXR0ZW1wdDNVdGMiKSkKICAgICAgICAgICAgcmVzdWx0LnJldHJ5QXR0ZW1wdDNVdGMgPSBkYXRhLnJldHJ5QXR0ZW1wdDNVdGM7CiAgICAgICAgcmVzdWx0LnJldHJ5SGFuZGxlZCA9IHRydWU7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlJldHJ5Q29uZmlybU1hcHBpbmc6IHJldHJ5IGJvdW5kYXJ5IGV4aGF1c3RlZCArIGlnbm9yZSBmYWxsYmFjayBjb25maXJtZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" } } ], @@ -359,8 +359,8 @@ "priority": 1 }, { - "action": 0, - "errorCodes": [ + "action": 5, + "errorTypes": [ "*" ], "priority": 999 diff --git a/core/Workflows/error-boundary/src/ErrorHttpMapping.csx b/core/Workflows/error-boundary/src/ErrorHttpMapping.csx deleted file mode 100644 index 2200775..0000000 --- a/core/Workflows/error-boundary/src/ErrorHttpMapping.csx +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Dynamic; -using System.Threading.Tasks; -using BBT.Workflow.Scripting; -using BBT.Workflow.Definitions; -using BBT.Workflow.Scripting.Functions; - -public class ErrorHttpMapping : ScriptBase, IMapping -{ - public async Task InputHandler(WorkflowTask task, ScriptContext context) - { - var httpTask = (task as HttpTask)!; - var data = context.Instance.Data; - string baseUrl = null; - try { baseUrl = GetConfigValue("MocklabBaseUrl"); } catch { } - if (!string.IsNullOrEmpty(baseUrl)) - { - httpTask.Url = baseUrl + "/api/test/error-endpoint"; - } - else - { - httpTask.Url = "http://mocklab:5000/api/test/error-endpoint"; - } - var body = new - { - source = "error-boundary-test", - testId = HasProperty(data, "testId") ? data.testId : null, - timestamp = DateTime.UtcNow.ToString("o") - }; - httpTask.SetBody(body); - return new ScriptResponse(); - } - - public async Task OutputHandler(ScriptContext context) - { - var data = context.Instance.Data; - var taskResponse = context.Body; - dynamic result = new ExpandoObject(); - if (HasProperty(data, "testId")) result.testId = data.testId; - if (HasProperty(data, "errorTestStarted")) result.errorTestStarted = data.errorTestStarted; - if (HasProperty(data, "startedAt")) result.startedAt = data.startedAt; - result.httpErrorHandled = true; - if (taskResponse != null) - { - result.httpStatusCode = taskResponse.statusCode; - result.httpIsSuccess = taskResponse.isSuccess; - if (taskResponse.data != null) - result.httpResponseData = taskResponse.data; - } - LogInformation("ErrorHttpMapping: output after HTTP call (error boundary may have retried)"); - return new ScriptResponse { Data = result }; - } -} diff --git a/core/Workflows/error-boundary/src/NotifyMapping.csx b/core/Workflows/error-boundary/src/NotifyMapping.csx deleted file mode 100644 index dff8b4e..0000000 --- a/core/Workflows/error-boundary/src/NotifyMapping.csx +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Dynamic; -using System.Threading.Tasks; -using BBT.Workflow.Scripting; -using BBT.Workflow.Definitions; - -public class NotifyMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public Task OutputHandler(ScriptContext context) - { - var data = context.Instance.Data; - dynamic result = new ExpandoObject(); - - if (HasProperty(data, "testId")) - result.testId = data.testId; - if (HasProperty(data, "retryCompleted")) - result.retryCompleted = data.retryCompleted; - if (HasProperty(data, "errorIgnored")) - result.errorIgnored = data.errorIgnored; - if (HasProperty(data, "rollbackExecuted")) - result.rollbackExecuted = data.rollbackExecuted; - if (HasProperty(data, "rollbackAt")) - result.rollbackAt = data.rollbackAt; - if (HasProperty(data, "logExecuted")) - result.logExecuted = data.logExecuted; - - result.notifyExecuted = true; - result.notifyAt = DateTime.UtcNow.ToString("o"); - - LogInformation("NotifyMapping completed - notify action test"); - - return Task.FromResult(new ScriptResponse { Data = result }); - } -} diff --git a/core/Workflows/error-boundary/src/RetryConfirmMapping.csx b/core/Workflows/error-boundary/src/RetryConfirmMapping.csx index a3e01b1..28f5d8f 100644 --- a/core/Workflows/error-boundary/src/RetryConfirmMapping.csx +++ b/core/Workflows/error-boundary/src/RetryConfirmMapping.csx @@ -2,6 +2,7 @@ using System; using System.Dynamic; using System.Threading.Tasks; using BBT.Workflow.Scripting; +using BBT.Workflow.Definitions; public class RetryConfirmMapping : ScriptBase, IMapping { @@ -14,9 +15,20 @@ public class RetryConfirmMapping : ScriptBase, IMapping { var data = context.Instance.Data; dynamic result = new ExpandoObject(); - if (HasProperty(data, "testId")) result.testId = data.testId; - if (HasProperty(data, "errorTestStarted")) result.errorTestStarted = data.errorTestStarted; - if (HasProperty(data, "startedAt")) result.startedAt = data.startedAt; + if (HasProperty(data, "testId")) + result.testId = data.testId; + if (HasProperty(data, "errorTestStarted")) + result.errorTestStarted = data.errorTestStarted; + if (HasProperty(data, "startedAt")) + result.startedAt = data.startedAt; + if (HasProperty(data, "retryThrowAttemptCount")) + result.retryThrowAttemptCount = data.retryThrowAttemptCount; + if (HasProperty(data, "retryAttempt1Utc")) + result.retryAttempt1Utc = data.retryAttempt1Utc; + if (HasProperty(data, "retryAttempt2Utc")) + result.retryAttempt2Utc = data.retryAttempt2Utc; + if (HasProperty(data, "retryAttempt3Utc")) + result.retryAttempt3Utc = data.retryAttempt3Utc; result.retryHandled = true; LogInformation("RetryConfirmMapping: retry boundary exhausted + ignore fallback confirmed"); return Task.FromResult(new ScriptResponse { Data = result }); diff --git a/core/Workflows/error-boundary/src/RetryThrowMapping.csx b/core/Workflows/error-boundary/src/RetryThrowMapping.csx new file mode 100644 index 0000000..72636d0 --- /dev/null +++ b/core/Workflows/error-boundary/src/RetryThrowMapping.csx @@ -0,0 +1,58 @@ +using System; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; +using BBT.Workflow.Definitions; + +/// +/// Same intentional failure as ThrowErrorMapping, but increments retryThrowAttemptCount +/// in InputHandler so each retry attempt is observable in instance data (before OutputHandler throws). +/// Also records ISO 8601 UTC stamps per attempt for backoff timing assertions. +/// +public class RetryThrowMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + var data = context.Instance.Data; + dynamic result = new ExpandoObject(); + + if (HasProperty(data, "testId")) + result.testId = data.testId; + if (HasProperty(data, "errorTestStarted")) + result.errorTestStarted = data.errorTestStarted; + if (HasProperty(data, "startedAt")) + result.startedAt = data.startedAt; + + var prior = 0; + if (HasProperty(data, "retryThrowAttemptCount")) + { + try + { + prior = Convert.ToInt32(data.retryThrowAttemptCount); + } + catch + { + prior = 0; + } + } + + var next = prior + 1; + result.retryThrowAttemptCount = next; + + var stamp = DateTime.UtcNow.ToString("o"); + if (next == 1) + result.retryAttempt1Utc = stamp; + else if (next == 2) + result.retryAttempt2Utc = stamp; + else if (next == 3) + result.retryAttempt3Utc = stamp; + + LogInformation($"RetryThrowMapping InputHandler attempt {next}"); + return Task.FromResult(new ScriptResponse { Data = result }); + } + + public Task OutputHandler(ScriptContext context) + { + throw new InvalidOperationException("Intentional test error for error boundary"); + } +} diff --git a/core/Workflows/error-boundary/src/RollbackMapping.csx b/core/Workflows/error-boundary/src/RollbackMapping.csx deleted file mode 100644 index 8d54fb9..0000000 --- a/core/Workflows/error-boundary/src/RollbackMapping.csx +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Dynamic; -using System.Threading.Tasks; -using BBT.Workflow.Scripting; -using BBT.Workflow.Definitions; - -public class RollbackMapping : ScriptBase, IMapping -{ - public Task InputHandler(WorkflowTask task, ScriptContext context) - { - return Task.FromResult(new ScriptResponse()); - } - - public Task OutputHandler(ScriptContext context) - { - var data = context.Instance.Data; - dynamic result = new ExpandoObject(); - - if (HasProperty(data, "testId")) - result.testId = data.testId; - if (HasProperty(data, "retryCompleted")) - result.retryCompleted = data.retryCompleted; - if (HasProperty(data, "errorIgnored")) - result.errorIgnored = data.errorIgnored; - - result.rollbackExecuted = true; - result.rollbackAt = DateTime.UtcNow.ToString("o"); - - LogInformation("RollbackMapping completed - rollback action test"); - - return Task.FromResult(new ScriptResponse { Data = result }); - } -} diff --git a/core/Workflows/flow-types/core-flow-test.json b/core/Workflows/flow-types/core-flow-test.json index 4fc52c3..b1bf636 100644 --- a/core/Workflows/flow-types/core-flow-test.json +++ b/core/Workflows/flow-types/core-flow-test.json @@ -4,7 +4,11 @@ "flowVersion": "1.0.0", "domain": "core", "version": "1.0.0", - "tags": ["integration-test", "flow-types", "core"], + "tags": [ + "integration-test", + "flow-types", + "core" + ], "attributes": { "type": "C", "timeout": null, @@ -115,4 +119,4 @@ } ] } -} +} \ No newline at end of file diff --git a/docs/integration-test-documentation.md b/docs/integration-test-documentation.md index d6771fc..24902be 100644 --- a/docs/integration-test-documentation.md +++ b/docs/integration-test-documentation.md @@ -470,106 +470,59 @@ extended-tasks-test-workflow (F) **Amac:** Hata yonetimi mekanizmasini (error boundary) task, state ve workflow seviyelerinde test eder; **Rollback (action:2)**, **Log (action:5)**, **Notify (action:4)**, **Abort (action:0)** ve ilgili aksiyonlari priority / errorTypes / errorCodes ile kapsar. **`errorBoundary.onTimeout` workflow taniminda yer almaz ve integration test ile dogrulanmaz** — runtime dokumantasyonunda da henuz implemente edilmedigi belirtilmistir; bkz. dokuman sonu. -### Agac Yapisi +### Agac Yapisi (ozet — tam tanim `error-boundary-test-workflow.json`) + +Tum state gecisleri script task (`error-script-task`) ile; HTTP task bu workflow'da kullanilmaz. ``` error-boundary-test-workflow (F) │ -├── [startTransition] start-error-boundary-test -│ └── onExecutionTasks: -│ └── error-script-task + InitErrorTestMapping.csx -│ -├── [workflow-level errorBoundary] -│ └── onError: action:0 (Abort), wildcard fallback (transition yok — schema kisiti) -│ (onTimeout tanimi YOK — test kapsami disi, bkz. dokuman sonu) -│ -├── retry-test-state (Initial, stateType:1) -│ ├── onEntries: -│ │ └── error-http-task (type:6) + ErrorHttpMapping.csx -│ │ └── [task-level errorBoundary] -│ │ └── onError: action:1 (Retry), priority:10 -│ │ └── retryPolicy: maxRetries:2, initialDelay:PT2S, backoffType:0 -│ └── Transitions: -│ └── auto-to-ignore-test (Auto) → ignore-test-state -│ └── rule: AlwaysTrueRule.csx -│ -├── ignore-test-state (Intermediate, stateType:2) -│ ├── [state-level errorBoundary] -│ │ └── onError: action:3 (Ignore), priority:10, errorTypes:["System.InvalidOperationException"], errorCodes:["*"] -│ ├── onEntries: -│ │ ├── order:1 error-script-task + ThrowErrorMapping.csx (kasitli hata firlatir) -│ │ └── order:2 error-script-task + IgnoreErrorMapping.csx (hata yoksayildiktan sonra calisir) -│ └── Transitions: -│ └── auto-to-rollback-from-ignore (Auto) → rollback-test-state -│ └── rule: AlwaysTrueRule.csx -│ -├── rollback-test-state (Intermediate, stateType:2) -│ ├── [state-level errorBoundary] -│ │ └── onError: action:2 (Rollback), priority:10 -│ ├── onEntries: -│ │ ├── order:1 error-script-task + ThrowErrorMapping.csx -│ │ └── order:2 error-script-task + RollbackMapping.csx (IMapping) -│ └── Transitions: -│ └── auto-to-log-from-rollback (Auto) → log-test-state -│ └── rule: AlwaysTrueRule.csx -│ -├── log-test-state (Intermediate, stateType:2) -│ ├── [state-level errorBoundary] -│ │ └── onError: action:5 (Log), priority:10 -│ ├── onEntries: -│ │ ├── order:1 error-script-task + ThrowErrorMapping.csx -│ │ └── order:2 error-script-task + LogOnlyMapping.csx -│ └── Transitions: -│ └── auto-to-completed-from-log (Auto) → notify-test-state -│ └── rule: AlwaysTrueRule.csx -│ -├── notify-test-state (Intermediate, stateType:2) -│ ├── [state-level errorBoundary] -│ │ └── onError: action:4 (Notify), priority:10, transition:"auto-to-completed-from-notify" -│ ├── onEntries: -│ │ ├── order:1 error-script-task + ThrowErrorMapping.csx -│ │ └── order:2 error-script-task + NotifyMapping.csx (IMapping) -│ └── Transitions: -│ └── auto-to-completed-from-notify (Auto) → completed-state -│ └── rule: AlwaysTrueRule.csx -│ -├── completed-state (Final/Success, stateType:3, subType:1) -└── error-final (Final/Error, stateType:3, subType:2) +├── [workflow-level errorBoundary] onError: action:0 (Abort), errorCodes:["*"] +├── [startTransition] → init-state +├── init-state → InitErrorTestMapping → auto → task-retry-state +├── task-retry-state → RetryThrowMapping (attempt sayaci + UTC damgalari) + [Retry→Ignore fallback] + RetryConfirmMapping → auto → task-ignore-state +├── task-ignore-state → ThrowErrorMapping + IgnoreErrorMapping → auto → task-log-state +├── task-log-state → ThrowErrorMapping + [Log logOnly] + LogOnlyMapping → auto → priority-rules-state +├── priority-rules-state → ThrowErrorMapping + [Ignore InvalidOperationException → wildcard Log] + PriorityConfirmMapping → auto → state-level-test-state +├── state-level-test-state → [state Ignore boundary] + ThrowErrorMapping + StateLevelConfirmMapping → auto → notify-test-state +├── notify-test-state → task Notify boundary (`transition`: notify-redirect) → rollback-test-state +├── rollback-test-state → task Rollback boundary (`transition`: rollback-redirect) → global-abort-test-state +├── global-abort-test-state → ThrowErrorMapping → workflow Abort → instance Faulted (`completed-state` tasarlanan akista ulasilmaz) +└── completed-state (final Success wiring — otomatik gecis pratikte ulasilmaz) ``` +(`errorBoundary.onTimeout` tanimi YOK — test kapsami disi, bkz. dokuman sonu.) + ### Test Edilen Ozellikler | Ozellik | Nasil Test Edilir | Ilgili Eleman | |---------|-------------------|---------------| -| Task-level error boundary | Task onEntries'de errorBoundary | retry-test-state onEntries | -| State-level error boundary | State uzerinde errorBoundary | ignore-test-state, rollback-test-state, log-test-state | -| Workflow-level error boundary | Workflow attributes'da errorBoundary | attributes.errorBoundary | -| Retry action (action:1) | HTTP task hata alinca yeniden dene | retry-test-state task errorBoundary | -| Retry policy | maxRetries, initialDelay, backoffType | retryPolicy yapisi | -| Ignore action (action:3) | Script hata firlatinca yoksay | ignore-test-state errorBoundary | -| **Rollback action (action:2)** | State errorBoundary rollback | rollback-test-state | -| **Log action (action:5)** | Hata loglama; siradaki task ile devam | log-test-state | -| **Notify action (action:4)** | Hata bildirim ve transition tetikleme | notify-test-state errorBoundary | -| Abort action (action:0) | Global wildcard fallback (transition kullanilmaz; instance Faulted) | workflow errorBoundary | -| **errorTypes / errorCodes** | Kural bazli eslestirme | priority-rules-state ve diger boundary kurallari | -| Priority siralama | Dusuk priority once calisir | task:10, state:10, workflow:100 | -| Hata sonrasi devam | Ignore sonrasi siradaki task calisir | IgnoreErrorMapping (order:2) | -| Rollback sonrasi zincir | rollback-test-state tamamlaninca log-test-state | auto-to-log-from-rollback | -| Log sonrasi basari finali | log-test-state tamamlaninca completed-state | auto-to-completed-from-log | +| Task-level error boundary | Task `onEntries` icinde `errorBoundary` | task-retry / ignore / log / priority / notify / rollback state tasklari | +| State-level error boundary | State `errorBoundary` | state-level-test-state | +| Workflow-level error boundary | Workflow `attributes.errorBoundary` | global Abort | +| Retry action (action:1) | Script task hatasinda yeniden dene | task-retry-state task boundary | +| Retry policy | maxRetries, initialDelay, backoffType | `retryPolicy` | +| Ignore action (action:3) | Hatayi yoksayip devam | task-ignore, priority-rules (tip eslestirmesi), state-level | +| **Rollback action (action:2)** | Task boundary + `transition` redirect | rollback-test-state | +| **Log action (action:5)** | logOnly / siradaki task | task-log-state, priority wildcard fallback | +| **Notify action (action:4)** | Task boundary + `transition` redirect | notify-test-state | +| Abort action (action:0) | Son hatada workflow boundary | global-abort-test-state sonrasi Faulted | +| **errorTypes** | Kural bazli eslestirme | priority-rules-state (`InvalidOperationException` vs `*`) | +| Priority siralama | Dusuk `priority` once | priority-rules-state ve diger kurallar | ### Kullanilan Bilesenler | Tip | Anahtar | Dosya | |-----|---------|-------| -| Task | `error-http-task` (type:6) | Tasks/error-boundary/error-http-task.json | | Task | `error-script-task` (type:7) | Tasks/error-boundary/error-script-task.json | | CSX | InitErrorTestMapping | Workflows/error-boundary/src/InitErrorTestMapping.csx | -| CSX | ErrorHttpMapping | Workflows/error-boundary/src/ErrorHttpMapping.csx | +| CSX | RetryThrowMapping | Workflows/error-boundary/src/RetryThrowMapping.csx | | CSX | ThrowErrorMapping | Workflows/error-boundary/src/ThrowErrorMapping.csx | +| CSX | RetryConfirmMapping | Workflows/error-boundary/src/RetryConfirmMapping.csx | | CSX | IgnoreErrorMapping | Workflows/error-boundary/src/IgnoreErrorMapping.csx | -| CSX | **RollbackMapping** | Workflows/error-boundary/src/RollbackMapping.csx | | CSX | LogOnlyMapping | Workflows/error-boundary/src/LogOnlyMapping.csx | -| CSX | **NotifyMapping** | Workflows/error-boundary/src/NotifyMapping.csx | +| CSX | PriorityConfirmMapping | Workflows/error-boundary/src/PriorityConfirmMapping.csx | +| CSX | StateLevelConfirmMapping | Workflows/error-boundary/src/StateLevelConfirmMapping.csx | | CSX | AlwaysTrueRule | Workflows/error-boundary/src/AlwaysTrueRule.csx | **Workflow tag'leri (ozet):** `rollback`, `log-action`, `notify`, `abort`, `priority`, `error-types`, `error-codes` dahil; tam liste `error-boundary-test-workflow.json` icindedir. diff --git a/tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryInstanceDataAssertions.cs b/tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryInstanceDataAssertions.cs index f92c13a..d7ad6fd 100644 --- a/tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryInstanceDataAssertions.cs +++ b/tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryInstanceDataAssertions.cs @@ -1,3 +1,4 @@ +using System.Globalization; using System.Text.Json; using Core.IntegrationTests.Helpers; using Xunit; @@ -10,6 +11,19 @@ namespace Core.IntegrationTests.Tests.ErrorBoundaryTestWorkflow; /// internal static class ErrorBoundaryInstanceDataAssertions { + /// + /// Matches task-retry-state: maxRetries: 2 → initial failure + 2 retries = 3 executions + /// before Ignore fallback (verify against runtime if this changes). + /// + private const int ExpectedRetryThrowAttempts = 3; + + /// + /// Backoff: initialDelay PT1S, then ×2 → ≥ ~1s and ≥ ~2s between InputHandler stamps (slack for CI). + /// + private const double MinMsBetweenAttempt1And2 = 750; + + private const double MinMsBetweenAttempt2And3 = 1600; + private const string ContractHint = "(error-boundary-test-workflow CSX contract)"; @@ -33,6 +47,72 @@ public static void AssertRetryHandled(JsonElement attributes) ); } + /// + /// Proves Retry ran multiple times via RetryThrowMapping counters, + /// and exponential backoff delays via UTC stamps (workflow: PT1S, multiplier 2, no jitter). + /// + public static void AssertRetryThrowAttemptsAndBackoff(JsonElement attributes) + { + Assert.True( + attributes.TryGetProperty("retryThrowAttemptCount", out var countEl), + $"retryThrowAttemptCount missing {ContractHint}" + ); + + Assert.True( + countEl.TryGetInt32(out var attempts), + $"retryThrowAttemptCount should be an integral JSON number {ContractHint}" + ); + + Assert.Equal( + ExpectedRetryThrowAttempts, + attempts + ); + + Assert.True( + attributes.TryGetProperty("retryAttempt1Utc", out var u1) + && u1.ValueKind == JsonValueKind.String, + $"retryAttempt1Utc missing {ContractHint}" + ); + Assert.True( + attributes.TryGetProperty("retryAttempt2Utc", out var u2) + && u2.ValueKind == JsonValueKind.String, + $"retryAttempt2Utc missing {ContractHint}" + ); + Assert.True( + attributes.TryGetProperty("retryAttempt3Utc", out var u3) + && u3.ValueKind == JsonValueKind.String, + $"retryAttempt3Utc missing {ContractHint}" + ); + + var t1 = DateTimeOffset.Parse( + u1.GetString()!, + CultureInfo.InvariantCulture, + DateTimeStyles.RoundtripKind + ); + var t2 = DateTimeOffset.Parse( + u2.GetString()!, + CultureInfo.InvariantCulture, + DateTimeStyles.RoundtripKind + ); + var t3 = DateTimeOffset.Parse( + u3.GetString()!, + CultureInfo.InvariantCulture, + DateTimeStyles.RoundtripKind + ); + + var ms12 = (t2 - t1).TotalMilliseconds; + var ms23 = (t3 - t2).TotalMilliseconds; + + Assert.True( + ms12 >= MinMsBetweenAttempt1And2, + $"Expected ≥{MinMsBetweenAttempt1And2}ms between retry InputHandler 1→2 (initialDelay PT1S), got {ms12}ms {ContractHint}" + ); + Assert.True( + ms23 >= MinMsBetweenAttempt2And3, + $"Expected ≥{MinMsBetweenAttempt2And3}ms between retry InputHandler 2→3 (backoff ×2), got {ms23}ms {ContractHint}" + ); + } + public static void AssertIgnoreExecuted(JsonElement attributes) { JsonElementAssertions.AssertPropertyTrue( @@ -53,7 +133,7 @@ public static void AssertPriorityRuleApplied(JsonElement attributes) { JsonElementAssertions.AssertPropertyTrue( attributes, "priorityRuleApplied", - $"priorityRuleApplied should be true (specific rule matched before fallback) {ContractHint}" + $"priorityRuleApplied should be true (InvalidOperationException → Ignore before wildcard Log fallback) {ContractHint}" ); } diff --git a/tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryTestWorkflowTests.cs b/tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryTestWorkflowTests.cs index 8e8e539..52a80f2 100644 --- a/tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryTestWorkflowTests.cs +++ b/tests/Core/Tests/error-boundary-test-workflow/ErrorBoundaryTestWorkflowTests.cs @@ -1,4 +1,3 @@ -using System.Text.Json; using Core.IntegrationTests.Helpers; using Core.IntegrationTests.Infrastructure; using Xunit; @@ -9,9 +8,9 @@ namespace Core.IntegrationTests.Tests.ErrorBoundaryTestWorkflow; /// Integration tests for error-boundary-test-workflow. /// Tests error boundary actions at task-level, state-level, and global-level. /// Actions tested: Retry (1), Rollback (2), Ignore (3), Notify (4), Log (5), Abort (0). -/// Flow: init → task-retry → task-ignore → task-log → priority-rules → state-level-test -/// → notify-test → [notify-redirect] → rollback-test → [rollback-redirect] -/// → global-abort-test → (Global Abort → Faulted) +/// Flow: init → task-retry → task-ignore → task-log → priority-rules (Ignore + wildcard Log) +/// → state-level-test → notify-test → [notify-redirect] → rollback-test → [rollback-redirect] +/// → global-abort-test → workflow Abort → Faulted (completed-state is not used on this path). /// public class ErrorBoundaryTestWorkflowTests : IntegrationTestBase { @@ -28,11 +27,13 @@ public ErrorBoundaryTestWorkflowTests(VNextTestEnvironment environment) } [Fact] - public async Task TaskLevel_RetryAction_RetriesExhaustedThenIgnoreFallback() + public async Task TaskLevel_RetryAction_MultipleAttemptsAndBackoffThenIgnoreFallback() { var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-retry"); + AssertTerminatedAtGlobalAbortFault(status, state); var attrs = await _workflow.GetAttributesAsync(instanceId); ErrorBoundaryInstanceDataAssertions.AssertInitMappingExecuted(attrs); + ErrorBoundaryInstanceDataAssertions.AssertRetryThrowAttemptsAndBackoff(attrs); ErrorBoundaryInstanceDataAssertions.AssertRetryHandled(attrs); } @@ -40,6 +41,7 @@ public async Task TaskLevel_RetryAction_RetriesExhaustedThenIgnoreFallback() public async Task TaskLevel_IgnoreAction_ErrorIgnoredNextTaskRuns() { var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-ignore"); + AssertTerminatedAtGlobalAbortFault(status, state); var attrs = await _workflow.GetAttributesAsync(instanceId); ErrorBoundaryInstanceDataAssertions.AssertInitMappingExecuted(attrs); ErrorBoundaryInstanceDataAssertions.AssertIgnoreExecuted(attrs); @@ -49,15 +51,17 @@ public async Task TaskLevel_IgnoreAction_ErrorIgnoredNextTaskRuns() public async Task TaskLevel_LogAction_ErrorLoggedNextTaskRuns() { var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-log"); + AssertTerminatedAtGlobalAbortFault(status, state); var attrs = await _workflow.GetAttributesAsync(instanceId); ErrorBoundaryInstanceDataAssertions.AssertInitMappingExecuted(attrs); ErrorBoundaryInstanceDataAssertions.AssertLogHandled(attrs); } [Fact] - public async Task TaskLevel_PriorityRules_SpecificRuleMatchesBeforeFallback() + public async Task TaskLevel_PriorityRules_IgnoreBeforeWildcardLogFallback() { var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-priority"); + AssertTerminatedAtGlobalAbortFault(status, state); var attrs = await _workflow.GetAttributesAsync(instanceId); ErrorBoundaryInstanceDataAssertions.AssertInitMappingExecuted(attrs); ErrorBoundaryInstanceDataAssertions.AssertPriorityRuleApplied(attrs); @@ -67,6 +71,7 @@ public async Task TaskLevel_PriorityRules_SpecificRuleMatchesBeforeFallback() public async Task StateLevel_IgnoreAction_ErrorHandledByStateBoundary() { var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-state-level"); + AssertTerminatedAtGlobalAbortFault(status, state); var attrs = await _workflow.GetAttributesAsync(instanceId); ErrorBoundaryInstanceDataAssertions.AssertStateLevelHandled(attrs); } @@ -75,26 +80,25 @@ public async Task StateLevel_IgnoreAction_ErrorHandledByStateBoundary() public async Task TaskLevel_NotifyAction_TransitionTriggered() { var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-notify"); - Assert.True( - state == "rollback-test-state" || state == "global-abort-test-state" || status == "F", - $"Notify action should redirect flow. Actual state='{state}', status='{status}'" - ); + AssertTerminatedAtGlobalAbortFault(status, state); } [Fact] public async Task TaskLevel_RollbackAction_TransitionTriggered() { var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-rollback"); - Assert.True( - state == "global-abort-test-state" || status == "F", - $"Rollback action should redirect flow. Actual state='{state}', status='{status}'" - ); + AssertTerminatedAtGlobalAbortFault(status, state); } [Fact] public async Task GlobalLevel_AbortAction_InstanceFaulted() { var (instanceId, status, state) = await StartAndWaitForFinalAsync("eb-global-abort"); + AssertTerminatedAtGlobalAbortFault(status, state); + } + + private static void AssertTerminatedAtGlobalAbortFault(string? status, string state) + { Assert.Equal("F", status); Assert.Equal("global-abort-test-state", state); } @@ -121,10 +125,8 @@ public async Task GlobalLevel_AbortAction_InstanceFaulted() currentState = StateFunctionJson.ExtractStateName(stateBody); status = StateFunctionJson.ExtractStatus(stateBody); - if (status == "F" || status == "C") - return (instanceId, status, currentState); - - if (currentState == "completed-state") + // This workflow is designed to fault at global-abort-test-state (workflow Abort), not to reach completed-state. + if (status == "F") return (instanceId, status, currentState); await Task.Delay(1000); From 533fc1efb6430a9f5ef2b4e77c10a7b8a025af0a Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Mon, 11 May 2026 17:44:20 +0300 Subject: [PATCH 23/24] subflow workflow tests are completed. --- .../subflow-orchestration.http | 188 ++++-- core.link.json | 2 +- .../lifecycle-transitions-test-workflow.json | 4 +- .../subflow-orchestration-child.diagram.json | 36 ++ ...flow-orchestration-grandchild.diagram.json | 24 + .../subflow-orchestration-parent.diagram.json | 28 +- ...Mapping.csx => ChildSharedMarkMapping.csx} | 11 +- ...> ParentSharedCommonTransitionMapping.csx} | 8 +- .../src/UpdateParentDataMapping.csx | 2 - .../subflow-orchestration-child.json | 550 ++++++++++-------- .../subflow-orchestration-parent.json | 10 +- docs/integration-test-documentation.md | 56 +- ...flowOrchestrationInstanceDataAssertions.cs | 133 +++++ .../SubflowOrchestrationScenarioActions.cs | 106 ++++ .../SubflowOrchestrationTestWorkflowTests.cs | 208 +++++++ vnext-example.sln.DotSettings.user | 7 + 16 files changed, 1028 insertions(+), 345 deletions(-) create mode 100644 core/Workflows/subflow-orchestration/.meta/subflow-orchestration-child.diagram.json create mode 100644 core/Workflows/subflow-orchestration/.meta/subflow-orchestration-grandchild.diagram.json rename core/Workflows/subflow-orchestration/src/{ChildSharedUpdateMapping.csx => ChildSharedMarkMapping.csx} (61%) rename core/Workflows/subflow-orchestration/src/{ParentSharedUpdateMapping.csx => ParentSharedCommonTransitionMapping.csx} (74%) create mode 100644 tests/Core/Tests/subflow-orchestration-test-workflow/SubflowOrchestrationInstanceDataAssertions.cs create mode 100644 tests/Core/Tests/subflow-orchestration-test-workflow/SubflowOrchestrationScenarioActions.cs create mode 100644 tests/Core/Tests/subflow-orchestration-test-workflow/SubflowOrchestrationTestWorkflowTests.cs create mode 100644 vnext-example.sln.DotSettings.user diff --git a/api-tests/subflow-orchestration/subflow-orchestration.http b/api-tests/subflow-orchestration/subflow-orchestration.http index c44d175..5dc9361 100644 --- a/api-tests/subflow-orchestration/subflow-orchestration.http +++ b/api-tests/subflow-orchestration/subflow-orchestration.http @@ -5,10 +5,8 @@ ############################################################################### # TEST 1: Happy path -# Start parent (autos to child then grandchild), complete grandchild on the -# parent instance URL, cascade to parent-completed. Verify data flags via -# /functions/data (parentStarted, childStarted, grandchildStarted, -# grandchildFinished, childCompleted as produced by mappings). +# Start parent (autos to child-manual-state), proceed-to-subflow (launches +# grandchild), complete grandchild, cascade to parent-completed. ############################################################################### ### TEST 1 — Step 1: Start parent workflow (sync) @@ -29,27 +27,34 @@ Content-Type: application/json @happyParentInstanceId = {{createHappyPathInstance.response.body.$.id}} -### TEST 1 — Step 2: State (deepest active should be grandchild-initial after autos) +### TEST 1 — Step 2: State (should be child-manual-state after autos) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyParentInstanceId}}/functions/state -### TEST 1 — Step 3: Complete grandchild (routed via parent instance + parent workflow key) +### TEST 1 — Step 3: Proceed to subflow (manual transition on child) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyParentInstanceId}}/transitions/proceed-to-subflow?sync=true +Content-Type: application/json + +{} + +### TEST 1 — Step 4: State (deepest active should be grandchild-initial) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyParentInstanceId}}/functions/state + +### TEST 1 — Step 5: Complete grandchild (routed via parent instance) PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyParentInstanceId}}/transitions/complete-grandchild?sync=true Content-Type: application/json {} -### TEST 1 — Step 4: State should reach parent-completed after cascade autos +### TEST 1 — Step 6: State should reach parent-completed after cascade autos GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyParentInstanceId}}/functions/state -### TEST 1 — Step 5: Instance data flags +### TEST 1 — Step 7: Instance data flags GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{happyParentInstanceId}}/functions/data ############################################################################### # TEST 2: Cancel cascade -# Start parent, confirm nested execution reached grandchild-initial, cancel the -# parent from the parent instance. Expect parent final state parent-cancelled -# (verify via /functions/state: state / metadata.currentState and cancel -# cascade into subflows per platform rules). +# Start parent, proceed to subflow so grandchild is running, then cancel +# parent. Expect parent-cancelled and cancel cascade into child/grandchild. ############################################################################### ### TEST 2 — Step 1: Start parent (sync) @@ -67,73 +72,85 @@ Content-Type: application/json @cancelCascadeParentInstanceId = {{createCancelCascadeInstance.response.body.$.id}} -### TEST 2 — Step 2: Wait until deepest state is grandchild-initial (poll until effectiveState or state reflects grandchild-initial; re-run if autos are slower) +### TEST 2 — Step 2: Proceed to subflow +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelCascadeParentInstanceId}}/transitions/proceed-to-subflow?sync=true +Content-Type: application/json + +{} + +### TEST 2 — Step 3: State should show grandchild-initial (nested) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelCascadeParentInstanceId}}/functions/state -### TEST 2 — Step 3: Cancel parent while nested (cascade should terminate child/grandchild definitions toward parent-cancelled) +### TEST 2 — Step 4: Cancel parent while nested (cascade should terminate child/grandchild) PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelCascadeParentInstanceId}}/transitions/cancel-parent?sync=true Content-Type: application/json {} -### TEST 2 — Step 4: Verify parent reached parent-cancelled (state function) +### TEST 2 — Step 5: Verify parent reached parent-cancelled GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{cancelCascadeParentInstanceId}}/functions/state ############################################################################### -# TEST 3: Shared transition from child workflow -# While nested, effective tracking should surface grandchild. Fire the child -# flow shared transition (shared-child-update) through the parent instance URL. +# TEST 3: Shared transition in allowed state (availableIn) +# Start parent → child reaches child-manual-state. Fire shared-child-mark +# (availableIn includes child-manual-state). Verify childSharedMarkExecuted. +# Then proceed to subflow, complete grandchild, verify final state. ############################################################################### ### TEST 3 — Step 1: Start parent (sync) -# @name createChildSharedInstance +# @name createSharedMarkInstance POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true Content-Type: application/json { - "key": "subflow-orch-child-shared-{{$timestamp}}", - "tags": ["integration-test", "subflow-orchestration", "child-shared"], + "key": "subflow-orch-shared-mark-{{$timestamp}}", + "tags": ["integration-test", "subflow-orchestration", "shared-transition"], "attributes": { - "testId": "orch-child-shared", + "testId": "orch-shared-mark", "parentData": { - "beforeChildShared": true + "beforeSharedMark": true } } } -@childSharedParentInstanceId = {{createChildSharedInstance.response.body.$.id}} +@sharedMarkParentInstanceId = {{createSharedMarkInstance.response.body.$.id}} -### TEST 3 — Step 2: Confirm nested / effective view shows grandchild-initial (effectiveState or nested state payload per runtime) -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{childSharedParentInstanceId}}/functions/state +### TEST 3 — Step 2: State (should be child-manual-state) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{sharedMarkParentInstanceId}}/functions/state -### TEST 3 — Step 3: Trigger child shared transition on parent instance (availableIn child-subflow-state) -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{childSharedParentInstanceId}}/transitions/shared-child-update?sync=true +### TEST 3 — Step 3: Trigger shared-child-mark (child is in child-manual-state → availableIn match) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{sharedMarkParentInstanceId}}/transitions/shared-child-mark?sync=true Content-Type: application/json -{ - "attributes": { - "message": "child-shared-from-http" - } -} +{} + +### TEST 3 — Step 4: Data should include childSharedMarkExecuted=true (child data) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{sharedMarkParentInstanceId}}/functions/data -### TEST 3 — Step 4: Data should include childSharedUpdateExecuted=true while still nested until grandchild completes -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{childSharedParentInstanceId}}/functions/data +### TEST 3 — Step 5: State should still be child-manual-state (target: $self) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{sharedMarkParentInstanceId}}/functions/state -### TEST 3 — Step 5: Complete grandchild then verify final cascade -PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{childSharedParentInstanceId}}/transitions/complete-grandchild?sync=true +### TEST 3 — Step 6: Proceed to subflow +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{sharedMarkParentInstanceId}}/transitions/proceed-to-subflow?sync=true Content-Type: application/json {} -### TEST 3 — Step 6: Final state and merged data -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{childSharedParentInstanceId}}/functions/state +### TEST 3 — Step 7: Complete grandchild then verify final cascade +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{sharedMarkParentInstanceId}}/transitions/complete-grandchild?sync=true +Content-Type: application/json -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{childSharedParentInstanceId}}/functions/data +{} + +### TEST 3 — Step 8: Final state and data +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{sharedMarkParentInstanceId}}/functions/state + +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{sharedMarkParentInstanceId}}/functions/data ############################################################################### # TEST 4: Effective state -# After start, /functions/state should expose effectiveState (deepest active -# subflow state, e.g. grandchild-initial) alongside the parent stack state. +# After start + proceed-to-subflow, /functions/state should expose +# effectiveState (deepest active subflow state, e.g. grandchild-initial). ############################################################################### ### TEST 4 — Step 1: Start parent (sync) @@ -151,18 +168,23 @@ Content-Type: application/json @effectiveStateParentInstanceId = {{createEffectiveStateInstance.response.body.$.id}} -### TEST 4 — Step 2: State function — verify effectiveState (and related metadata if present) shows deepest active subflow state (grandchild-initial) +### TEST 4 — Step 2: Proceed to subflow +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{effectiveStateParentInstanceId}}/transitions/proceed-to-subflow?sync=true +Content-Type: application/json + +{} + +### TEST 4 — Step 3: State function — verify effectiveState shows grandchild-initial GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{effectiveStateParentInstanceId}}/functions/state -### TEST 4 — Step 3: Optional data snapshot while nested +### TEST 4 — Step 4: Optional data snapshot while nested GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{effectiveStateParentInstanceId}}/functions/data ############################################################################### # TEST 5: UpdateData transition from SubFlow -# Start parent, wait until nested in child-subflow-state, trigger the -# updateData transition (update-parent-data) on the child. This should -# update the parent instance data with childUpdatedParent=true via -# UpdateParentDataMapping.csx. Target is $self so child stays in same state. +# Start parent, proceed to subflow (grandchild running), trigger updateData +# (update-parent-data) from child. This updates parent instance data with +# childUpdatedParent=true via UpdateParentDataMapping.csx. ############################################################################### ### TEST 5 — Step 1: Start parent (sync) @@ -180,10 +202,16 @@ Content-Type: application/json @updateDataParentInstanceId = {{createUpdateDataInstance.response.body.$.id}} -### TEST 5 — Step 2: Verify nested state (child-subflow-state or grandchild-initial) +### TEST 5 — Step 2: Proceed to subflow +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{updateDataParentInstanceId}}/transitions/proceed-to-subflow?sync=true +Content-Type: application/json + +{} + +### TEST 5 — Step 3: Verify nested state (grandchild-initial) GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{updateDataParentInstanceId}}/functions/state -### TEST 5 — Step 3: Trigger updateData transition from child +### TEST 5 — Step 4: Trigger updateData transition from child PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{updateDataParentInstanceId}}/transitions/update-parent-data?sync=true Content-Type: application/json @@ -193,14 +221,68 @@ Content-Type: application/json } } -### TEST 5 — Step 4: Verify parent data now includes childUpdatedParent=true +### TEST 5 — Step 5: Verify parent data now includes childUpdatedParent=true GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{updateDataParentInstanceId}}/functions/data -### TEST 5 — Step 5: Complete grandchild to finish the flow +### TEST 5 — Step 6: Complete grandchild to finish the flow PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{updateDataParentInstanceId}}/transitions/complete-grandchild?sync=true Content-Type: application/json {} -### TEST 5 — Step 6: Verify final state and data +### TEST 5 — Step 7: Verify final state GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{updateDataParentInstanceId}}/functions/state + +############################################################################### +# TEST 6: Shared transition in DISALLOWED state (NOT in availableIn) +# Start parent → proceed to subflow (child reaches child-subflow-state, +# grandchild is running). Try shared-child-mark — child is in +# child-subflow-state which is NOT in availableIn. Transition should be +# rejected (state should not change). Then complete grandchild normally. +############################################################################### + +### TEST 6 — Step 1: Start parent (sync) +# @name createSharedDisallowedInstance +POST {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/start?sync=true +Content-Type: application/json + +{ + "key": "subflow-orch-shared-disallowed-{{$timestamp}}", + "tags": ["integration-test", "subflow-orchestration", "shared-transition-disallowed"], + "attributes": { + "testId": "orch-shared-disallowed" + } +} + +@sharedDisallowedParentInstanceId = {{createSharedDisallowedInstance.response.body.$.id}} + +### TEST 6 — Step 2: Proceed to subflow (child → child-subflow-state, grandchild starts) +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{sharedDisallowedParentInstanceId}}/transitions/proceed-to-subflow?sync=true +Content-Type: application/json + +{} + +### TEST 6 — Step 3: State should show grandchild-initial (nested) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{sharedDisallowedParentInstanceId}}/functions/state + +### TEST 6 — Step 4: Try shared-child-mark (child is in child-subflow-state — NOT in availableIn) +# Expected: transition rejected or not found; state should NOT change +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{sharedDisallowedParentInstanceId}}/transitions/shared-child-mark?sync=true +Content-Type: application/json + +{} + +### TEST 6 — Step 5: Verify state is still grandchild-initial (unchanged — shared transition rejected) +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{sharedDisallowedParentInstanceId}}/functions/state + +### TEST 6 — Step 6: Data should NOT contain childSharedMarkExecuted +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{sharedDisallowedParentInstanceId}}/functions/data + +### TEST 6 — Step 7: Complete grandchild normally +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{sharedDisallowedParentInstanceId}}/transitions/complete-grandchild?sync=true +Content-Type: application/json + +{} + +### TEST 6 — Step 8: Verify final state parent-completed +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{workflow}}/instances/{{sharedDisallowedParentInstanceId}}/functions/state diff --git a/core.link.json b/core.link.json index 89011eb..b277919 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-05-08T13:15:19.217Z" + "importedAt": "2026-05-11T14:35:14.679Z" } \ No newline at end of file diff --git a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json index 6b54861..63b9ea9 100644 --- a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json +++ b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json @@ -52,8 +52,6 @@ "target": "terminated-state", "triggerType": 0, "versionStrategy": "Major", - "onExecutionTasks": [], - "availableIn": [], "labels": [ { "language": "en-US", @@ -501,4 +499,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-child.diagram.json b/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-child.diagram.json new file mode 100644 index 0000000..609bb4a --- /dev/null +++ b/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-child.diagram.json @@ -0,0 +1,36 @@ +{ + "nodePos": { + "__start__": { + "x": 90, + "y": 30 + }, + "child-initial": { + "x": 50, + "y": 210 + }, + "child-subflow-state": { + "x": -40, + "y": 470 + }, + "child-completed": { + "x": 30, + "y": 740 + }, + "child-cancelled": { + "x": -280, + "y": 220 + }, + "__wf_updateData__": { + "x": 280, + "y": 100 + }, + "__wf_cancel__": { + "x": -200, + "y": 55 + }, + "__wf_shared_shared-child-update__": { + "x": -370, + "y": 520 + } + } +} \ No newline at end of file diff --git a/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-grandchild.diagram.json b/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-grandchild.diagram.json new file mode 100644 index 0000000..0471b6a --- /dev/null +++ b/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-grandchild.diagram.json @@ -0,0 +1,24 @@ +{ + "nodePos": { + "__start__": { + "x": 90, + "y": 30 + }, + "grandchild-initial": { + "x": 0, + "y": 220 + }, + "grandchild-completed": { + "x": 0, + "y": 440 + }, + "grandchild-cancelled": { + "x": -350, + "y": 180 + }, + "__wf_cancel__": { + "x": -200, + "y": 0 + } + } +} \ No newline at end of file diff --git a/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-parent.diagram.json b/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-parent.diagram.json index 9e8a142..5c43df5 100644 --- a/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-parent.diagram.json +++ b/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-parent.diagram.json @@ -1,24 +1,36 @@ { "nodePos": { "parent-initial": { - "x": 496, - "y": 240 + "x": 640, + "y": 160 }, "parent-subflow-state": { - "x": 940, - "y": 480 + "x": 970, + "y": 490 }, "parent-completed": { "x": 1348, "y": 240 }, "parent-cancelled": { - "x": 100, - "y": 100 + "x": 270, + "y": 430 }, "__start__": { - "x": 100, - "y": 240 + "x": 710, + "y": -130 + }, + "__wf_shared_shared-update-data__": { + "x": 480, + "y": 610 + }, + "__wf_cancel__": { + "x": 350, + "y": 300 + }, + "__wf_shared_shared-common-transition__": { + "x": 570, + "y": 570 } } } \ No newline at end of file diff --git a/core/Workflows/subflow-orchestration/src/ChildSharedUpdateMapping.csx b/core/Workflows/subflow-orchestration/src/ChildSharedMarkMapping.csx similarity index 61% rename from core/Workflows/subflow-orchestration/src/ChildSharedUpdateMapping.csx rename to core/Workflows/subflow-orchestration/src/ChildSharedMarkMapping.csx index 54795bb..4305f8c 100644 --- a/core/Workflows/subflow-orchestration/src/ChildSharedUpdateMapping.csx +++ b/core/Workflows/subflow-orchestration/src/ChildSharedMarkMapping.csx @@ -4,7 +4,7 @@ using System.Threading.Tasks; using BBT.Workflow.Definitions; using BBT.Workflow.Scripting; -public class ChildSharedUpdateMapping : ScriptBase, IMapping +public class ChildSharedMarkMapping : ScriptBase, IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) { @@ -16,10 +16,11 @@ public class ChildSharedUpdateMapping : ScriptBase, IMapping var data = context.Instance.Data; dynamic result = new ExpandoObject(); if (HasProperty(data, "childStarted")) result.childStarted = data.childStarted; - if (HasProperty(data, "grandchildCompleted")) result.grandchildCompleted = data.grandchildCompleted; - result.childSharedUpdateExecuted = true; - result.childSharedUpdateAt = DateTime.UtcNow.ToString("o"); - LogInformation("ChildSharedUpdateMapping completed"); + if (HasProperty(data, "parentData")) result.parentData = data.parentData; + if (HasProperty(data, "testId")) result.testId = data.testId; + result.childSharedMarkExecuted = true; + result.childSharedMarkAt = DateTime.UtcNow.ToString("o"); + LogInformation("ChildSharedMarkMapping completed"); return Task.FromResult(new ScriptResponse { Data = result }); } } diff --git a/core/Workflows/subflow-orchestration/src/ParentSharedUpdateMapping.csx b/core/Workflows/subflow-orchestration/src/ParentSharedCommonTransitionMapping.csx similarity index 74% rename from core/Workflows/subflow-orchestration/src/ParentSharedUpdateMapping.csx rename to core/Workflows/subflow-orchestration/src/ParentSharedCommonTransitionMapping.csx index 4f3f389..4eedec6 100644 --- a/core/Workflows/subflow-orchestration/src/ParentSharedUpdateMapping.csx +++ b/core/Workflows/subflow-orchestration/src/ParentSharedCommonTransitionMapping.csx @@ -4,7 +4,7 @@ using System.Threading.Tasks; using BBT.Workflow.Definitions; using BBT.Workflow.Scripting; -public class ParentSharedUpdateMapping : ScriptBase, IMapping +public class ParentSharedCommonTransitionMapping : ScriptBase, IMapping { public Task InputHandler(WorkflowTask task, ScriptContext context) { @@ -23,8 +23,10 @@ public class ParentSharedUpdateMapping : ScriptBase, IMapping target[kv.Key] = kv.Value; } } - target["sharedUpdateExecuted"] = true; - LogInformation("ParentSharedUpdateMapping: merged instance data with sharedUpdateExecuted"); + target["sharedCommonTransitionExecuted"] = true; + LogInformation( + "ParentSharedCommonTransitionMapping: merged instance data with sharedCommonTransitionExecuted" + ); return Task.FromResult(new ScriptResponse { Data = result }); } } diff --git a/core/Workflows/subflow-orchestration/src/UpdateParentDataMapping.csx b/core/Workflows/subflow-orchestration/src/UpdateParentDataMapping.csx index 3ca3f72..0917a5b 100644 --- a/core/Workflows/subflow-orchestration/src/UpdateParentDataMapping.csx +++ b/core/Workflows/subflow-orchestration/src/UpdateParentDataMapping.csx @@ -20,8 +20,6 @@ public class UpdateParentDataMapping : ScriptBase, IMapping result.childStarted = data.childStarted; if (HasProperty(data, "grandchildFinished")) result.grandchildFinished = data.grandchildFinished; - if (HasProperty(data, "childSharedUpdateExecuted")) - result.childSharedUpdateExecuted = data.childSharedUpdateExecuted; result.childUpdatedParent = true; result.updateParentAt = DateTime.UtcNow.ToString("o"); diff --git a/core/Workflows/subflow-orchestration/subflow-orchestration-child.json b/core/Workflows/subflow-orchestration/subflow-orchestration-child.json index c4221fb..96c055d 100644 --- a/core/Workflows/subflow-orchestration/subflow-orchestration-child.json +++ b/core/Workflows/subflow-orchestration/subflow-orchestration-child.json @@ -1,242 +1,310 @@ { - "key": "subflow-orchestration-child", - "flow": "sys-flows", - "flowVersion": "1.0.0", - "domain": "core", - "version": "1.0.0", - "tags": [ - "integration-test", - "subflow-orchestration", - "child", - "subflow", - "update-data" - ], - "attributes": { - "type": "S", - "timeout": null, - "labels": [ - { - "language": "en-US", - "label": "SubFlow Orchestration Child" - } - ], - "functions": [], - "features": [], - "extensions": [], - "sharedTransitions": [ - { - "key": "shared-child-update", - "target": "$self", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Shared Child Update ($self)" - } - ], - "availableIn": [ - "child-subflow-state" - ], - "onExecutionTasks": [ - { - "order": 1, - "task": { - "key": "subflow-script-task", - "domain": "core", - "version": "1.0.0", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/ChildSharedUpdateMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIENoaWxkU2hhcmVkVXBkYXRlTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY2hpbGRTdGFydGVkIikpIHJlc3VsdC5jaGlsZFN0YXJ0ZWQgPSBkYXRhLmNoaWxkU3RhcnRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdyYW5kY2hpbGRDb21wbGV0ZWQiKSkgcmVzdWx0LmdyYW5kY2hpbGRDb21wbGV0ZWQgPSBkYXRhLmdyYW5kY2hpbGRDb21wbGV0ZWQ7CiAgICAgICAgcmVzdWx0LmNoaWxkU2hhcmVkVXBkYXRlRXhlY3V0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC5jaGlsZFNoYXJlZFVwZGF0ZUF0ID0gRGF0ZVRpbWUuVXRjTm93LlRvU3RyaW5nKCJvIik7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkNoaWxkU2hhcmVkVXBkYXRlTWFwcGluZyBjb21wbGV0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" - } - } - ] - } - ], - "cancel": { - "key": "cancel-child", - "target": "child-cancelled", - "triggerType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Cancel Child" - } - ] - }, - "updateData": { - "key": "update-parent-data", - "target": "$self", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Update Parent Data from Child" - } - ], - "onExecutionTasks": [ - { - "order": 1, - "task": { - "key": "subflow-script-task", - "domain": "core", - "version": "1.0.0", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/UpdateParentDataMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFVwZGF0ZVBhcmVudERhdGFNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY2hpbGRTdGFydGVkIikpCiAgICAgICAgICAgIHJlc3VsdC5jaGlsZFN0YXJ0ZWQgPSBkYXRhLmNoaWxkU3RhcnRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdyYW5kY2hpbGRGaW5pc2hlZCIpKQogICAgICAgICAgICByZXN1bHQuZ3JhbmRjaGlsZEZpbmlzaGVkID0gZGF0YS5ncmFuZGNoaWxkRmluaXNoZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJjaGlsZFNoYXJlZFVwZGF0ZUV4ZWN1dGVkIikpCiAgICAgICAgICAgIHJlc3VsdC5jaGlsZFNoYXJlZFVwZGF0ZUV4ZWN1dGVkID0gZGF0YS5jaGlsZFNoYXJlZFVwZGF0ZUV4ZWN1dGVkOwoKICAgICAgICByZXN1bHQuY2hpbGRVcGRhdGVkUGFyZW50ID0gdHJ1ZTsKICAgICAgICByZXN1bHQudXBkYXRlUGFyZW50QXQgPSBEYXRlVGltZS5VdGNOb3cuVG9TdHJpbmcoIm8iKTsKCiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlVwZGF0ZVBhcmVudERhdGFNYXBwaW5nIGNvbXBsZXRlZCAtIHVwZGF0ZURhdGEgZnJvbSBjaGlsZCB0byBwYXJlbnQiKTsKCiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UgeyBEYXRhID0gcmVzdWx0IH0pOwogICAgfQp9Cg==" - } - } - ] - }, - "startTransition": { - "key": "start-subflow-orchestration-child", - "target": "child-initial", - "triggerType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Start Child" - } - ] - }, - "states": [ - { - "key": "child-initial", - "stateType": 1, - "subType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Child Initial" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - { - "order": 1, - "task": { - "key": "subflow-script-task", - "domain": "core", - "version": "1.0.0", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/ChildStartMapping.csx", - "code": "dXNpbmcgU3lzdGVtLkR5bmFtaWM7CnVzaW5nIFN5c3RlbS5UaHJlYWRpbmcuVGFza3M7CnVzaW5nIEJCVC5Xb3JrZmxvdy5EZWZpbml0aW9uczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBDaGlsZFN0YXJ0TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC5jaGlsZFN0YXJ0ZWQgPSB0cnVlOwogICAgICAgIGlmIChkYXRhICE9IG51bGwpCiAgICAgICAgewogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInBhcmVudERhdGEiKSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcmVzdWx0LnBhcmVudERhdGEgPSBkYXRhLnBhcmVudERhdGE7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIExvZ0luZm9ybWF0aW9uKCJDaGlsZFN0YXJ0TWFwcGluZzogY2hpbGRTdGFydGVkIHNldCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" - } - } - ], - "onExits": [], - "transitions": [ - { - "key": "auto-child-to-subflow", - "target": "child-subflow-state", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Auto to Child SubFlow State" - } - ], - "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" - }, - "onExecutionTasks": [] - } - ] - }, - { - "key": "child-subflow-state", - "stateType": 4, - "subType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Child SubFlow State" - } - ], - "view": null, - "subFlow": { - "type": "S", - "process": { - "key": "subflow-orchestration-grandchild", - "domain": "core", - "version": "1.0.0", - "flow": "sys-flows" - }, - "mapping": { - "location": "./src/ChildToGrandchildSubFlowMapping.csx", - "code": "dXNpbmcgU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWM7CnVzaW5nIFN5c3RlbS5EeW5hbWljOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIENoaWxkVG9HcmFuZGNoaWxkU3ViRmxvd01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJU3ViRmxvd01hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyBnY0lucHV0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoZGF0YSAhPSBudWxsKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJwYXJlbnREYXRhIikpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGdjSW5wdXQucGFyZW50RGF0YSA9IGRhdGEucGFyZW50RGF0YTsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBnY0lucHV0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY2hpbGRTdGFydGVkIikpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGdjSW5wdXQuY2hpbGRTdGFydGVkID0gZGF0YS5jaGlsZFN0YXJ0ZWQ7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkNoaWxkVG9HcmFuZGNoaWxkU3ViRmxvd01hcHBpbmc6IHByZXBhcmVkIGdyYW5kY2hpbGQgaW5wdXQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSBnY0lucHV0IH0pOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgYm9keSA9IGNvbnRleHQuQm9keSBhcyBJRGljdGlvbmFyeTxzdHJpbmcsIG9iamVjdD47CiAgICAgICAgZHluYW1pYyBtZXJnZWQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHZhciB0YXJnZXQgPSAoSURpY3Rpb25hcnk8c3RyaW5nLCBvYmplY3Q+KW1lcmdlZDsKICAgICAgICB2YXIgaW5zdCA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YSBhcyBJRGljdGlvbmFyeTxzdHJpbmcsIG9iamVjdD47CiAgICAgICAgaWYgKGluc3QgIT0gbnVsbCkKICAgICAgICB7CiAgICAgICAgICAgIGZvcmVhY2ggKHZhciBrdiBpbiBpbnN0KQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICB0YXJnZXRba3YuS2V5XSA9IGt2LlZhbHVlOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIGlmIChib2R5ICE9IG51bGwpCiAgICAgICAgewogICAgICAgICAgICBmb3JlYWNoICh2YXIga3YgaW4gYm9keSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgdGFyZ2V0W2t2LktleV0gPSBrdi5WYWx1ZTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICB0YXJnZXRbImdyYW5kY2hpbGRDb21wbGV0ZWQiXSA9IHRydWU7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkNoaWxkVG9HcmFuZGNoaWxkU3ViRmxvd01hcHBpbmc6IG1lcmdlZCBncmFuZGNoaWxkIG91dHB1dCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IG1lcmdlZCB9KTsKICAgIH0KfQo=" - } - }, - "onEntries": [], - "onExits": [], - "transitions": [ - { - "key": "auto-child-to-completed", - "target": "child-completed", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Auto to Child Completed" - } - ], - "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" - }, - "onExecutionTasks": [] - } - ] - }, - { - "key": "child-completed", - "stateType": 3, - "subType": 1, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Child Completed" - } - ], - "view": null, - "subFlow": null, - "onEntries": [], - "onExits": [], - "transitions": [] - }, - { - "key": "child-cancelled", - "stateType": 3, - "subType": 3, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Child Cancelled" - } - ], - "view": null, - "subFlow": null, - "onEntries": [], - "onExits": [], - "transitions": [] - } - ] - } -} \ No newline at end of file + "key": "subflow-orchestration-child", + "flow": "sys-flows", + "flowVersion": "1.0.0", + "domain": "core", + "version": "1.0.0", + "tags": [ + "integration-test", + "subflow-orchestration", + "child", + "subflow", + "shared-transitions" + ], + "attributes": { + "type": "S", + "timeout": null, + "labels": [ + { + "language": "en-US", + "label": "SubFlow Orchestration Child" + } + ], + "functions": [ + + ], + "features": [ + + ], + "extensions": [ + + ], + "sharedTransitions": [ + { + "key": "shared-child-mark", + "target": "$self", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Shared Child Mark ($self)" + } + ], + "availableIn": [ + "child-manual-state" + ], + "onExecutionTasks": [ + { + "order": 1, + "task": { + "key": "subflow-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/ChildSharedMarkMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIENoaWxkU2hhcmVkTWFya01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNoaWxkU3RhcnRlZCIpKSByZXN1bHQuY2hpbGRTdGFydGVkID0gZGF0YS5jaGlsZFN0YXJ0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJwYXJlbnREYXRhIikpIHJlc3VsdC5wYXJlbnREYXRhID0gZGF0YS5wYXJlbnREYXRhOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICByZXN1bHQuY2hpbGRTaGFyZWRNYXJrRXhlY3V0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC5jaGlsZFNoYXJlZE1hcmtBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwogICAgICAgIExvZ0luZm9ybWF0aW9uKCJDaGlsZFNoYXJlZE1hcmtNYXBwaW5nIGNvbXBsZXRlZCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" + } + } + ] + } + ], + "cancel": { + "key": "cancel-child", + "target": "child-cancelled", + "triggerType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Cancel Child" + } + ] + }, + "updateData": { + "key": "update-parent-data", + "target": "$self", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Update Parent Data from Child" + } + ], + "availableIn": [ + + ], + "onExecutionTasks": [ + { + "order": 1, + "task": { + "key": "subflow-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/UpdateParentDataMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFVwZGF0ZVBhcmVudERhdGFNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY2hpbGRTdGFydGVkIikpCiAgICAgICAgICAgIHJlc3VsdC5jaGlsZFN0YXJ0ZWQgPSBkYXRhLmNoaWxkU3RhcnRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdyYW5kY2hpbGRGaW5pc2hlZCIpKQogICAgICAgICAgICByZXN1bHQuZ3JhbmRjaGlsZEZpbmlzaGVkID0gZGF0YS5ncmFuZGNoaWxkRmluaXNoZWQ7CgogICAgICAgIHJlc3VsdC5jaGlsZFVwZGF0ZWRQYXJlbnQgPSB0cnVlOwogICAgICAgIHJlc3VsdC51cGRhdGVQYXJlbnRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiVXBkYXRlUGFyZW50RGF0YU1hcHBpbmcgY29tcGxldGVkIC0gdXBkYXRlRGF0YSBmcm9tIGNoaWxkIHRvIHBhcmVudCIpOwoKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" + } + } + ] + }, + "startTransition": { + "key": "start-subflow-orchestration-child", + "target": "child-initial", + "triggerType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Start Child" + } + ] + }, + "states": [ + { + "key": "child-initial", + "stateType": 1, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Child Initial" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "subflow-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/ChildStartMapping.csx", + "code": "dXNpbmcgU3lzdGVtLkR5bmFtaWM7CnVzaW5nIFN5c3RlbS5UaHJlYWRpbmcuVGFza3M7CnVzaW5nIEJCVC5Xb3JrZmxvdy5EZWZpbml0aW9uczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBDaGlsZFN0YXJ0TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC5jaGlsZFN0YXJ0ZWQgPSB0cnVlOwogICAgICAgIGlmIChkYXRhICE9IG51bGwpCiAgICAgICAgewogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInBhcmVudERhdGEiKSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcmVzdWx0LnBhcmVudERhdGEgPSBkYXRhLnBhcmVudERhdGE7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIExvZ0luZm9ybWF0aW9uKCJDaGlsZFN0YXJ0TWFwcGluZzogY2hpbGRTdGFydGVkIHNldCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" + } + } + ], + "onExits": [ + + ], + "transitions": [ + { + "key": "auto-child-to-manual", + "target": "child-manual-state", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to Child Manual State" + } + ], + "rule": { + "location": "./src/AlwaysTrueRule.csx", + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" + }, + "onExecutionTasks": [ + + ] + } + ] + }, + { + "key": "child-manual-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Child Manual State" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + + ], + "onExits": [ + + ], + "transitions": [ + { + "key": "proceed-to-subflow", + "target": "child-subflow-state", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Proceed to SubFlow" + } + ], + "onExecutionTasks": [ + + ] + } + ] + }, + { + "key": "child-subflow-state", + "stateType": 4, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Child SubFlow State" + } + ], + "view": null, + "subFlow": { + "type": "S", + "process": { + "key": "subflow-orchestration-grandchild", + "domain": "core", + "version": "1.0.0", + "flow": "sys-flows" + }, + "mapping": { + "location": "./src/ChildToGrandchildSubFlowMapping.csx", + "code": "dXNpbmcgU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWM7CnVzaW5nIFN5c3RlbS5EeW5hbWljOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIENoaWxkVG9HcmFuZGNoaWxkU3ViRmxvd01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJU3ViRmxvd01hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyBnY0lucHV0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoZGF0YSAhPSBudWxsKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJwYXJlbnREYXRhIikpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGdjSW5wdXQucGFyZW50RGF0YSA9IGRhdGEucGFyZW50RGF0YTsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBnY0lucHV0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY2hpbGRTdGFydGVkIikpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGdjSW5wdXQuY2hpbGRTdGFydGVkID0gZGF0YS5jaGlsZFN0YXJ0ZWQ7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkNoaWxkVG9HcmFuZGNoaWxkU3ViRmxvd01hcHBpbmc6IHByZXBhcmVkIGdyYW5kY2hpbGQgaW5wdXQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSBnY0lucHV0IH0pOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgYm9keSA9IGNvbnRleHQuQm9keSBhcyBJRGljdGlvbmFyeTxzdHJpbmcsIG9iamVjdD47CiAgICAgICAgZHluYW1pYyBtZXJnZWQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHZhciB0YXJnZXQgPSAoSURpY3Rpb25hcnk8c3RyaW5nLCBvYmplY3Q+KW1lcmdlZDsKICAgICAgICB2YXIgaW5zdCA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YSBhcyBJRGljdGlvbmFyeTxzdHJpbmcsIG9iamVjdD47CiAgICAgICAgaWYgKGluc3QgIT0gbnVsbCkKICAgICAgICB7CiAgICAgICAgICAgIGZvcmVhY2ggKHZhciBrdiBpbiBpbnN0KQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICB0YXJnZXRba3YuS2V5XSA9IGt2LlZhbHVlOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIGlmIChib2R5ICE9IG51bGwpCiAgICAgICAgewogICAgICAgICAgICBmb3JlYWNoICh2YXIga3YgaW4gYm9keSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgdGFyZ2V0W2t2LktleV0gPSBrdi5WYWx1ZTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICB0YXJnZXRbImdyYW5kY2hpbGRDb21wbGV0ZWQiXSA9IHRydWU7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkNoaWxkVG9HcmFuZGNoaWxkU3ViRmxvd01hcHBpbmc6IG1lcmdlZCBncmFuZGNoaWxkIG91dHB1dCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IG1lcmdlZCB9KTsKICAgIH0KfQo=" + } + }, + "onEntries": [ + + ], + "onExits": [ + + ], + "transitions": [ + { + "key": "auto-child-to-completed", + "target": "child-completed", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to Child Completed" + } + ], + "rule": { + "location": "./src/AlwaysTrueRule.csx", + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" + }, + "onExecutionTasks": [ + + ] + } + ] + }, + { + "key": "child-completed", + "stateType": 3, + "subType": 1, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Child Completed" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + + ], + "onExits": [ + + ], + "transitions": [ + + ] + }, + { + "key": "child-cancelled", + "stateType": 3, + "subType": 3, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Child Cancelled" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + + ], + "onExits": [ + + ], + "transitions": [ + + ] + } + ] + } +} diff --git a/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json b/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json index 66e38c0..13caa32 100644 --- a/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json +++ b/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json @@ -25,14 +25,14 @@ "extensions": [], "sharedTransitions": [ { - "key": "shared-update-data", + "key": "shared-common-transition", "target": "$self", "triggerType": 0, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Shared Update Data" + "label": "Shared Common Transition" } ], "availableIn": [ @@ -48,8 +48,8 @@ "flow": "sys-tasks" }, "mapping": { - "location": "./src/ParentSharedUpdateMapping.csx", - "code": "dXNpbmcgU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWM7CnVzaW5nIFN5c3RlbS5EeW5hbWljOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CgpwdWJsaWMgY2xhc3MgUGFyZW50U2hhcmVkVXBkYXRlTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHZhciB0YXJnZXQgPSAoSURpY3Rpb25hcnk8c3RyaW5nLCBvYmplY3Q+KXJlc3VsdDsKICAgICAgICB2YXIgaW5zdCA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YSBhcyBJRGljdGlvbmFyeTxzdHJpbmcsIG9iamVjdD47CiAgICAgICAgaWYgKGluc3QgIT0gbnVsbCkKICAgICAgICB7CiAgICAgICAgICAgIGZvcmVhY2ggKHZhciBrdiBpbiBpbnN0KQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICB0YXJnZXRba3YuS2V5XSA9IGt2LlZhbHVlOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIHRhcmdldFsic2hhcmVkVXBkYXRlRXhlY3V0ZWQiXSA9IHRydWU7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIlBhcmVudFNoYXJlZFVwZGF0ZU1hcHBpbmc6IG1lcmdlZCBpbnN0YW5jZSBkYXRhIHdpdGggc2hhcmVkVXBkYXRlRXhlY3V0ZWQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" + "location": "./src/ParentSharedCommonTransitionMapping.csx", + "code": "dXNpbmcgU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWM7CnVzaW5nIFN5c3RlbS5EeW5hbWljOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CgpwdWJsaWMgY2xhc3MgUGFyZW50U2hhcmVkQ29tbW9uVHJhbnNpdGlvbk1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICB2YXIgdGFyZ2V0ID0gKElEaWN0aW9uYXJ5PHN0cmluZywgb2JqZWN0PilyZXN1bHQ7CiAgICAgICAgdmFyIGluc3QgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGEgYXMgSURpY3Rpb25hcnk8c3RyaW5nLCBvYmplY3Q+OwogICAgICAgIGlmIChpbnN0ICE9IG51bGwpCiAgICAgICAgewogICAgICAgICAgICBmb3JlYWNoICh2YXIga3YgaW4gaW5zdCkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgdGFyZ2V0W2t2LktleV0gPSBrdi5WYWx1ZTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICB0YXJnZXRbInNoYXJlZENvbW1vblRyYW5zaXRpb25FeGVjdXRlZCJdID0gdHJ1ZTsKICAgICAgICBMb2dJbmZvcm1hdGlvbigKICAgICAgICAgICAgIlBhcmVudFNoYXJlZENvbW1vblRyYW5zaXRpb25NYXBwaW5nOiBtZXJnZWQgaW5zdGFuY2UgZGF0YSB3aXRoIHNoYXJlZENvbW1vblRyYW5zaXRpb25FeGVjdXRlZCIKICAgICAgICApOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" } } ] @@ -179,7 +179,7 @@ } }, "transitions": { - "auto-child-to-subflow": { + "auto-child-to-manual": { "roles": [ { "role": "morph-idm.maker", diff --git a/docs/integration-test-documentation.md b/docs/integration-test-documentation.md index 24902be..261e4ce 100644 --- a/docs/integration-test-documentation.md +++ b/docs/integration-test-documentation.md @@ -9,7 +9,7 @@ Bu dokuman, `core` projesindeki tum integration test workflow'larini, icerdikler | # | Grup | Workflow | Test Edilen Ana Ozellikler | |---|------|----------|---------------------------| | 1 | Lifecycle & Transitions | `lifecycle-transitions-test-workflow` | State tipleri, transition tipleri, on-entries/exits, timer, cancel, **exit** (`attributes.exit`), **schedule iptal / timer reschedule**, **queryRoles** ve **transition `roles` (yalnizca state fonksiyonu listeleme filtresi)** | -| 2 | SubFlow & SubProcess | `subflow-orchestration-parent` + child + grandchild | Parent-child-grandchild zinciri, parent shared transitions, **child shared transition**, **cancel cascade** (child/grandchild **cancelled** final state'leri), **effective state**, **updateData (SubFlow)**, **subFlow.overrides** (timeout, states.queryRoles, transitions.roles) | +| 2 | SubFlow & SubProcess | `subflow-orchestration-parent` + child + grandchild | Parent-child-grandchild zinciri, parent shared transitions, **child shared transition (availableIn dogrulamasi)**, **shared transition disallowed state testi**, **cancel cascade** (child/grandchild **cancelled** final state'leri), **effective state**, **updateData (SubFlow)**, **subFlow.overrides** (timeout, states.queryRoles, transitions.roles) | | 3 | Task Execution | `task-execution-test-workflow` + `task-target-workflow` + `extended-tasks-test-workflow` | HTTP/Script/StartFlow/GetInstanceData/Notification/TriggerTransition/SubProcess/GetInstances; Dapr HTTP/Service/PubSub (`extended-tasks-test-workflow`); Mocklab + Dapr YAML | | 4 | Error Boundary | `error-boundary-test-workflow` | Task/state/global errorBoundary; Retry/Ignore/Log/Rollback/Notify/Abort; retry policy; priority; errorTypes/errorCodes. **`errorBoundary.onTimeout` bu flow'da test edilmez** (bkz. dokuman sonu: Test Edilmeyen Ozellikler) | | 5 | View, Function & Extension (Comprehensive) | `view-function-extension-test-workflow` | 6 view tipi (JSON/HTML/MD/DeepLink/HTTP/URN), 6 display modu, 4 extension tipi (4 type × 3 scope), 3 function scope (I/F/D), wizard state, implicit global extension, `?extensions=` sorgusu, `functions/view` icerik dogrulamasi | @@ -187,7 +187,7 @@ lifecycle-transitions-test-workflow (F) - `subflow-orchestration-child` (type: S) - Alt flow - `subflow-orchestration-grandchild` (type: S) - Torun flow -**Amac:** Parent-child-grandchild SubFlow zincirini, parent seviyesindeki shared transition'lari, **child workflow uzerindeki shared transition'u**, **cancel cascade** (parent iptalinde alt akislarda **child-cancelled** / **grandchild-cancelled** final state'leri), **`/functions/state` uzerinden effective state** gorunurlugunu, **updateData transition** (SubFlow'dan parent data guncelleme) ozelligini ve **`subFlow.overrides`** mekanizmasini (parent'tan child workflow'a timeout, state `queryRoles` ve transition `roles` override etme) test eder. +**Amac:** Parent-child-grandchild SubFlow zincirini, parent seviyesindeki shared transition'lari, **child workflow uzerindeki shared transition'u (availableIn dogrulamasi dahil)**, **shared transition'in availableIn disinda kalan state'te reddedilmesini**, **cancel cascade** (parent iptalinde alt akislarda **child-cancelled** / **grandchild-cancelled** final state'leri), **`/functions/state` uzerinden effective state** gorunurlugunu, **updateData transition** (SubFlow'dan parent data guncelleme) ozelligini ve **`subFlow.overrides`** mekanizmasini (parent'tan child workflow'a timeout, state `queryRoles` ve transition `roles` override etme) test eder. ### Agac Yapisi @@ -201,10 +201,10 @@ subflow-orchestration-parent (F) ├── [cancel] cancel-parent → parent-cancelled │ ├── [sharedTransitions] -│ └── shared-update-data (Manual, target:$self) -│ ├── availableIn: ["parent-subflow-state"] -│ └── onExecutionTasks: -│ └── subflow-script-task + ParentSharedUpdateMapping.csx +│ └── shared-common-transition (Manual, target:$self) + │ ├── availableIn: ["parent-subflow-state"] + │ └── onExecutionTasks: + │ └── subflow-script-task + ParentSharedCommonTransitionMapping.csx │ ├── parent-initial (Initial, stateType:1) │ └── Transitions: @@ -218,7 +218,7 @@ subflow-orchestration-parent (F) │ │ └── overrides: │ │ ├── timeout: child-push-timeout → child-cancelled (PT15M, OnEntry) │ │ ├── states.child-initial.queryRoles: [morph-idm.maker=allow, morph-idm.viewer=deny] -│ │ └── transitions.auto-child-to-subflow.roles: [morph-idm.maker=allow, morph-idm.viewer=deny] +│ │ └── transitions.auto-child-to-manual.roles: [morph-idm.maker=allow, morph-idm.viewer=deny] │ └── Transitions: │ └── auto-parent-to-completed (Auto) → parent-completed │ └── rule: AlwaysTrueRule.csx @@ -235,10 +235,10 @@ subflow-orchestration-parent (F) │ └── subflow-script-task + UpdateParentDataMapping.csx (IMapping) │ ├── [sharedTransitions] - │ └── shared-child-update (Manual, target:$self) - │ ├── availableIn: ["child-subflow-state"] + │ └── shared-child-mark (Manual, target:$self) + │ ├── availableIn: ["child-manual-state"] │ └── onExecutionTasks: - │ └── subflow-script-task + ChildSharedUpdateMapping.csx (IMapping) + │ └── subflow-script-task + ChildSharedMarkMapping.csx (IMapping) │ ├── [startTransition] start-subflow-orchestration-child → child-initial │ @@ -246,9 +246,13 @@ subflow-orchestration-parent (F) │ ├── onEntries: │ │ └── subflow-script-task + ChildStartMapping.csx │ └── Transitions: - │ └── auto-child-to-subflow (Auto) → child-subflow-state + │ └── auto-child-to-manual (Auto) → child-manual-state │ └── rule: AlwaysTrueRule.csx │ + ├── child-manual-state (Intermediate, stateType:2) + │ └── Transitions: + │ └── proceed-to-subflow (Manual) → child-subflow-state + │ ├── child-subflow-state (SubFlow, stateType:4) │ ├── subFlow: type "S" │ │ ├── process: subflow-orchestration-grandchild @@ -287,28 +291,31 @@ subflow-orchestration-parent (F) | SubFlow tipi (type: S) | Child ve grandchild workflow tipleri | subflow-orchestration-child/grandchild | | 3 seviye zincirleme (parent→child→grandchild) | Ic ice SubFlow cagrilari | Tum 3 workflow | | ISubFlowMapping | SubFlow'a data gonderme/alma | ParentToChildSubFlowMapping, ChildToGrandchildSubFlowMapping | -| Shared transitions ($self) — parent | State degistirmeden data guncelleme | shared-update-data | -| **Shared transitions ($self) — child** | Child tanimindaki paylasimli gecis; **availableIn: child-subflow-state** | shared-child-update, ChildSharedUpdateMapping.csx | -| availableIn kisitlamasi | Shared transition sadece belirli state'lerde | parent: parent-subflow-state; child: child-subflow-state | +| Shared transitions ($self) — parent | State degistirmeden data guncelleme | shared-common-transition | +| **Shared transitions ($self) — child** | Child tanimindaki paylasimli gecis; **availableIn: child-manual-state** | shared-child-mark, ChildSharedMarkMapping.csx | +| availableIn kisitlamasi | Shared transition sadece belirli state'lerde | parent: parent-subflow-state; child: child-manual-state | +|| **Shared transition disallowed state** | availableIn'de bulunmayan state'te shared transition reddedilir | shared-child-mark child-subflow-state'te (availableIn'de yok) | +|| Manuel transition (child icinde) | Child subflow'unda kullanici tetikli gecis | proceed-to-subflow (child-manual-state → child-subflow-state) | | Cancel transition — parent | Parent iptal | cancel-parent → parent-cancelled | | **Cancel hedefleri — child / grandchild** | Alt akislarda iptal final state | cancel-child → **child-cancelled**; cancel-grandchild → **grandchild-cancelled** | | **Cancel cascade** | Parent iptalinde ic ice sonlandirma | HTTP: cancel-parent senaryosu | | **Effective state** | `/functions/state` ile en derin aktif alt akis durumu (or. grandchild-initial) | HTTP Test 4 | | **UpdateData transition (SubFlow)** | Child'dan parent instance datasini guncelleme ($self) | update-parent-data, UpdateParentDataMapping.csx | -| Manuel transition (SubFlow icinde) | Grandchild'da kullanici tetikli tamamlama | complete-grandchild | +| Manuel transition (grandchild icinde) | Grandchild'da kullanici tetikli tamamlama | complete-grandchild | | **subFlow.overrides.timeout** | Parent'tan child'a timeout override (PT15M, OnEntry, → child-cancelled) | parent-subflow-state.subFlow.overrides.timeout | | **subFlow.overrides.states.queryRoles** | Parent'tan child-initial state'inin queryRoles'unu ezme | parent-subflow-state.subFlow.overrides.states.child-initial | -| **subFlow.overrides.transitions.roles** | Parent'tan auto-child-to-subflow transition'inin roles'unu ezme | parent-subflow-state.subFlow.overrides.transitions.auto-child-to-subflow | +| **subFlow.overrides.transitions.roles** | Parent'tan auto-child-to-manual transition'inin roles'unu ezme | parent-subflow-state.subFlow.overrides.transitions.auto-child-to-manual | ### HTTP Test Dosyasi (`subflow-orchestration.http`) | Test | Kisa Aciklama | |------|---------------| -| TEST 1 Happy path | Grandchild tamamlama, parent-completed ve data bayraklari | -| **TEST 2 Cancel cascade** | Parent cancel; alt akislarin sonlanmasi ve parent-cancelled | -| **TEST 3 Child shared transition** | Parent instance URL uzerinden **shared-child-update**; `childSharedUpdateExecuted` | -| **TEST 4 Effective state** | nested calisma sirasinda effectiveState / metadata | -| **TEST 5 UpdateData SubFlow** | Child updateData tetikleme; parent data'da `childUpdatedParent` kontrolu | +| TEST 1 Happy path | proceed-to-subflow + grandchild tamamlama, parent-completed ve data bayraklari | +| **TEST 2 Cancel cascade** | proceed-to-subflow sonrasi parent cancel; alt akislarin sonlanmasi ve parent-cancelled | +| **TEST 3 Shared transition (allowed state)** | child-manual-state'te **shared-child-mark** tetikleme; `childSharedMarkExecuted` dogrulamasi | +| **TEST 4 Effective state** | proceed-to-subflow sonrasi nested calisma sirasinda effectiveState / metadata | +| **TEST 5 UpdateData SubFlow** | proceed-to-subflow sonrasi child updateData tetikleme; parent data'da `childUpdatedParent` kontrolu | +| **TEST 6 Shared transition (disallowed state)** | child-subflow-state'te (availableIn'de yok) **shared-child-mark** tetikleme; reddedilmeli, state degismemeli | ### Kullanilan Bilesenler @@ -317,10 +324,10 @@ subflow-orchestration-parent (F) | Task | `subflow-script-task` | Tasks/subflow-orchestration/subflow-script-task.json | | CSX | ParentStartMapping | Workflows/subflow-orchestration/src/ParentStartMapping.csx | | CSX | ParentToChildSubFlowMapping | Workflows/subflow-orchestration/src/ParentToChildSubFlowMapping.csx | -| CSX | ParentSharedUpdateMapping | Workflows/subflow-orchestration/src/ParentSharedUpdateMapping.csx | +| CSX | ParentSharedCommonTransitionMapping | Workflows/subflow-orchestration/src/ParentSharedCommonTransitionMapping.csx | | CSX | ChildStartMapping | Workflows/subflow-orchestration/src/ChildStartMapping.csx | | CSX | ChildToGrandchildSubFlowMapping | Workflows/subflow-orchestration/src/ChildToGrandchildSubFlowMapping.csx | -| CSX | **ChildSharedUpdateMapping** | Workflows/subflow-orchestration/src/ChildSharedUpdateMapping.csx | +| CSX | **ChildSharedMarkMapping** | Workflows/subflow-orchestration/src/ChildSharedMarkMapping.csx | | CSX | **UpdateParentDataMapping** | Workflows/subflow-orchestration/src/UpdateParentDataMapping.csx | | CSX | GrandchildStartMapping | Workflows/subflow-orchestration/src/GrandchildStartMapping.csx | | CSX | GrandchildCompleteMapping | Workflows/subflow-orchestration/src/GrandchildCompleteMapping.csx | @@ -1359,7 +1366,8 @@ Her grubun hangi vNext ozelliklerini test ettigini gosteren matris. **Gn** sutun | **Schedule cancel (manuel)** | X | | | | | | | | | | | **Timer reschedule (self-loop)** | X | | | | | | | | | | | Shared transitions ($self) | | X | | | | | | | | | -| **Child-level shared transition** | | X | | | | | | | | | +| **Child-level shared transition (availableIn)** | | X | | | | | | | | | +|| **Shared transition disallowed state** | | X | | | | | | | | | | **SubFlow cancel final states (child/grandchild)** | | X | | | | | | | | | | **Effective state (/functions/state, nested)** | | X | | | | | | | | | | updateData ($self) | | X | | | | X | | | | | diff --git a/tests/Core/Tests/subflow-orchestration-test-workflow/SubflowOrchestrationInstanceDataAssertions.cs b/tests/Core/Tests/subflow-orchestration-test-workflow/SubflowOrchestrationInstanceDataAssertions.cs new file mode 100644 index 0000000..8b64ef4 --- /dev/null +++ b/tests/Core/Tests/subflow-orchestration-test-workflow/SubflowOrchestrationInstanceDataAssertions.cs @@ -0,0 +1,133 @@ +using System.Text.Json; +using Core.IntegrationTests.Helpers; +using Xunit; + +namespace Core.IntegrationTests.Tests.SubflowOrchestrationTestWorkflow; + +/// +/// Attribute/data contract checks for the subflow orchestration workflow family. +/// +internal static class SubflowOrchestrationInstanceDataAssertions +{ + private const string ContractHint = + "(subflow-orchestration mappings + api-tests/subflow-orchestration.http expected flags)"; + + public static void AssertParentStarted(JsonElement attributes, string expectedTestId) + { + JsonElementAssertions.AssertPropertyTrue(attributes, "parentStarted", ContractHint); + JsonElementAssertions.AssertPropertyString(attributes, "testId", expectedTestId, ContractHint); + } + + public static void AssertHappyPathCompleted(JsonElement attributes, string expectedTestId) + { + AssertParentStarted(attributes, expectedTestId); + JsonElementAssertions.AssertPropertyTrue(attributes, "childStarted", ContractHint); + JsonElementAssertions.AssertPropertyTrue(attributes, "grandchildFinished", ContractHint); + JsonElementAssertions.AssertPropertyTrue(attributes, "grandchildCompleted", ContractHint); + JsonElementAssertions.AssertPropertyTrue(attributes, "childCompleted", ContractHint); + } + + public static void AssertParentCommonTransitionExecuted(JsonElement attributes) + { + JsonElementAssertions.AssertPropertyTrue( + attributes, + "sharedCommonTransitionExecuted", + "Parent shared-common-transition should mark the parent instance data." + ); + AssertPropertyMissing( + attributes, + "childUpdatedParent", + "Parent common transition must not perform child updateData behavior." + ); + } + + public static void AssertChildSharedMarkExecuted(JsonElement dataBody) + { + var data = ExtractDataPayload(dataBody); + JsonElementAssertions.AssertPropertyTrue( + data, + "childSharedMarkExecuted", + "Child shared-child-mark should mark the current child data." + ); + JsonElementAssertions.AssertPropertyNonEmptyString( + data, + "childSharedMarkAt", + "Child shared-child-mark should record an execution timestamp." + ); + } + + public static void AssertChildSharedMarkNotExecuted(JsonElement dataBody) + { + var data = ExtractDataPayload(dataBody); + AssertPropertyMissing( + data, + "childSharedMarkExecuted", + "shared-child-mark must not execute outside its availableIn states." + ); + } + + public static void AssertUpdateDataApplied(JsonElement attributes) + { + JsonElementAssertions.AssertPropertyTrue( + attributes, + "childUpdatedParent", + "update-parent-data should update parent instance data from the child SubFlow." + ); + JsonElementAssertions.AssertPropertyNonEmptyString( + attributes, + "updateParentAt", + "update-parent-data should record an update timestamp on parent data." + ); + AssertPropertyMissing( + attributes, + "childSharedMarkExecuted", + "updateData must stay separate from child shared transition markers." + ); + AssertPropertyMissing( + attributes, + "sharedCommonTransitionExecuted", + "updateData must stay separate from parent common transition markers." + ); + } + + public static void AssertGrandchildStartedData(JsonElement dataBody) + { + var data = ExtractDataPayload(dataBody); + JsonElementAssertions.AssertPropertyTrue( + data, + "grandchildStarted", + "functions/data should expose deepest active grandchild data while nested." + ); + JsonElementAssertions.AssertPropertyTrue( + data, + "childStarted", + "Grandchild input should preserve childStarted from the child SubFlow." + ); + } + + public static void AssertPropertyMissing( + JsonElement parent, + string name, + string? message = null + ) + { + Assert.False( + parent.ValueKind == JsonValueKind.Object && parent.TryGetProperty(name, out _), + message ?? $"Property '{name}' should not be present." + ); + } + + private static JsonElement ExtractDataPayload(JsonElement body) + { + if ( + body.ValueKind == JsonValueKind.Object + && body.TryGetProperty("data", out var data) + && data.ValueKind == JsonValueKind.Object + ) + { + return data; + } + + return body; + } +} diff --git a/tests/Core/Tests/subflow-orchestration-test-workflow/SubflowOrchestrationScenarioActions.cs b/tests/Core/Tests/subflow-orchestration-test-workflow/SubflowOrchestrationScenarioActions.cs new file mode 100644 index 0000000..f02f89e --- /dev/null +++ b/tests/Core/Tests/subflow-orchestration-test-workflow/SubflowOrchestrationScenarioActions.cs @@ -0,0 +1,106 @@ +using System.Net.Http; +using Core.IntegrationTests.Helpers; + +namespace Core.IntegrationTests.Tests.SubflowOrchestrationTestWorkflow; + +/// +/// Scenario actions for subflow-orchestration-parent. +/// Keeps workflow-specific state and transition literals out of generic helpers. +/// +internal sealed class SubflowOrchestrationScenarioActions +{ + public const string ChildManualState = "child-manual-state"; + public const string GrandchildInitialState = "grandchild-initial"; + public const string ParentCompletedState = "parent-completed"; + public const string ParentCancelledState = "parent-cancelled"; + + private const string ProceedToSubflowTransition = "proceed-to-subflow"; + private const string CompleteGrandchildTransition = "complete-grandchild"; + private const string CancelParentTransition = "cancel-parent"; + private const string ParentCommonTransition = "shared-common-transition"; + private const string ChildSharedMarkTransition = "shared-child-mark"; + private const string UpdateParentDataTransition = "update-parent-data"; + + private readonly WorkflowInstanceTestHelper _wf; + private readonly TimeSpan _timeout; + + public SubflowOrchestrationScenarioActions( + WorkflowInstanceTestHelper wf, + TimeSpan timeout + ) + { + _wf = wf; + _timeout = timeout; + } + + public async Task StartAsync(string testId, params string[] extraTags) + { + var tags = new List { "integration-test", "subflow-orchestration" }; + tags.AddRange(extraTags); + + var id = await _wf.StartInstanceIdAsync( + new + { + key = WorkflowInstanceTestHelper.UniqueInstanceKey("subflow-orch-it"), + tags = tags.ToArray(), + attributes = new { testId }, + } + ); + + await _wf.WaitForStateAsync(id, ChildManualState, _timeout); + return id; + } + + public async Task ProceedToGrandchildAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, ProceedToSubflowTransition, headers: null); + await _wf.WaitForStateAsync(instanceId, GrandchildInitialState, _timeout); + } + + public async Task CompleteGrandchildAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, CompleteGrandchildTransition, headers: null); + await _wf.WaitForStateAsync(instanceId, ParentCompletedState, _timeout); + } + + public async Task CancelParentAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, CancelParentTransition, headers: null); + await _wf.WaitForStateAsync(instanceId, ParentCancelledState, _timeout); + } + + public async Task RunParentCommonTransitionAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, ParentCommonTransition, headers: null); + await _wf.WaitForStateAsync(instanceId, ChildManualState, _timeout); + } + + public async Task RunChildSharedMarkAsync(string instanceId) + { + await _wf.RunTransitionAsync(instanceId, ChildSharedMarkTransition, headers: null); + await _wf.WaitForStateAsync(instanceId, ChildManualState, _timeout); + } + + public async Task TryRunChildSharedMarkFromCurrentStateAsync(string instanceId) + { + try + { + await _wf.RunTransitionAsync(instanceId, ChildSharedMarkTransition, headers: null); + return true; + } + catch (HttpRequestException) + { + return false; + } + } + + public async Task RunUpdateParentDataAsync(string instanceId, string updateMessage) + { + await _wf.RunTransitionAsync( + instanceId, + UpdateParentDataTransition, + headers: null, + new { attributes = new { updateMessage } } + ); + } +} diff --git a/tests/Core/Tests/subflow-orchestration-test-workflow/SubflowOrchestrationTestWorkflowTests.cs b/tests/Core/Tests/subflow-orchestration-test-workflow/SubflowOrchestrationTestWorkflowTests.cs new file mode 100644 index 0000000..b119315 --- /dev/null +++ b/tests/Core/Tests/subflow-orchestration-test-workflow/SubflowOrchestrationTestWorkflowTests.cs @@ -0,0 +1,208 @@ +using Core.IntegrationTests.Helpers; +using Core.IntegrationTests.Infrastructure; +using Xunit; + +namespace Core.IntegrationTests.Tests.SubflowOrchestrationTestWorkflow; + +/* +! Bugs: +! UpdateData: parent endpoint should resolve child workflow updateData while child SubFlow is active. +! Runtime currently returns Instance:100024 ("subflow-orchestration-parent does not define an updateData configuration"). +*/ + +/// +/// Integration tests aligned with api-tests/subflow-orchestration/subflow-orchestration.http. +/// Covers parent-child-grandchild SubFlow routing, shared transitions, updateData, cancel cascade, +/// and effective state exposure through the parent instance. +/// +public class SubflowOrchestrationTestWorkflowTests : IntegrationTestBase +{ + private const string WorkflowKey = "subflow-orchestration-parent"; + + private static readonly TimeSpan StateTimeout = TimeSpan.FromMinutes(1); + + private readonly WorkflowInstanceTestHelper _wf; + private readonly SubflowOrchestrationScenarioActions _scenario; + + public SubflowOrchestrationTestWorkflowTests(VNextTestEnvironment environment) + : base(environment) + { + _wf = new WorkflowInstanceTestHelper(Api, WorkflowKey); + _scenario = new SubflowOrchestrationScenarioActions(_wf, StateTimeout); + } + + [Fact] + public async Task HappyPath_ParentChildGrandchild_ReachesParentCompleted() + { + const string testId = "subflow-orch-happy-path"; + var instanceId = await _scenario.StartAsync(testId, "happy-path"); + + await _scenario.ProceedToGrandchildAsync(instanceId); + + await _scenario.CompleteGrandchildAsync(instanceId); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal( + SubflowOrchestrationScenarioActions.ParentCompletedState, + StateFunctionJson.ExtractStateName(stateBody) + ); + Assert.Equal("C", StateFunctionJson.ExtractStatus(stateBody)); + + var attributes = await _wf.GetAttributesAsync(instanceId); + SubflowOrchestrationInstanceDataAssertions.AssertHappyPathCompleted(attributes, testId); + } + + [Fact] + public async Task ParentCommonTransition_InParentSubflowState_MarksParentDataOnly() + { + var instanceId = await _scenario.StartAsync( + "subflow-orch-parent-common-transition", + "parent-common-transition" + ); + + await _scenario.RunParentCommonTransitionAsync(instanceId); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal( + SubflowOrchestrationScenarioActions.ChildManualState, + StateFunctionJson.ExtractStateName(stateBody) + ); + + var attributes = await _wf.GetAttributesAsync(instanceId); + SubflowOrchestrationInstanceDataAssertions.AssertParentCommonTransitionExecuted(attributes); + } + + [Fact] + public async Task ChildSharedTransition_InAllowedManualState_MergesMarkerAfterCompletion() + { + var instanceId = await _scenario.StartAsync( + "subflow-orch-child-shared-mark", + "shared-transition" + ); + + var stateBeforeShared = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal( + SubflowOrchestrationScenarioActions.ChildManualState, + StateFunctionJson.ExtractStateName(stateBeforeShared) + ); + if (!StateFunctionJson.TransitionsContainKey(stateBeforeShared, "shared-child-mark")) + { + Assert.Fail( + "TODO: BUG: shared-child-mark should be available while effective state is child-manual-state. " + + "subflow-orchestration-child.sharedTransitions.availableIn includes child-manual-state, " + + "but functions/state does not expose the shared transition." + ); + } + + await _scenario.RunChildSharedMarkAsync(instanceId); + + var stateAfterShared = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal( + SubflowOrchestrationScenarioActions.ChildManualState, + StateFunctionJson.ExtractStateName(stateAfterShared) + ); + + var attributes = await _wf.GetAttributesAsync(instanceId); + SubflowOrchestrationInstanceDataAssertions.AssertPropertyMissing( + attributes, + "childUpdatedParent", + "shared-child-mark must not update parent data through updateData." + ); + + await _scenario.ProceedToGrandchildAsync(instanceId); + await _scenario.CompleteGrandchildAsync(instanceId); + + var completedAttributes = await _wf.GetAttributesAsync(instanceId); + SubflowOrchestrationInstanceDataAssertions.AssertChildSharedMarkExecuted( + completedAttributes + ); + SubflowOrchestrationInstanceDataAssertions.AssertPropertyMissing( + completedAttributes, + "childUpdatedParent", + "shared-child-mark must remain separate from updateData after child output is merged." + ); + } + + [Fact] + public async Task ChildSharedTransition_InDisallowedSubflowState_DoesNotExecute() + { + var instanceId = await _scenario.StartAsync( + "subflow-orch-child-shared-disallowed", + "shared-transition-disallowed" + ); + await _scenario.ProceedToGrandchildAsync(instanceId); + + // Runtime may reject the transition at HTTP level or accept it as a no-op. + // The contract under test is that it must not execute outside availableIn. + _ = await _scenario.TryRunChildSharedMarkFromCurrentStateAsync(instanceId); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal( + SubflowOrchestrationScenarioActions.GrandchildInitialState, + StateFunctionJson.ExtractStateName(stateBody) + ); + + var dataBody = await _wf.CallFunctionAsync(instanceId, "data"); + SubflowOrchestrationInstanceDataAssertions.AssertChildSharedMarkNotExecuted(dataBody); + + await _scenario.CompleteGrandchildAsync(instanceId); + } + + [Fact] + public async Task EffectiveState_AfterProceedToSubflow_ExposesGrandchildInitial() + { + var instanceId = await _scenario.StartAsync( + "subflow-orch-effective-state", + "effective-state" + ); + + await _scenario.ProceedToGrandchildAsync(instanceId); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal( + SubflowOrchestrationScenarioActions.GrandchildInitialState, + StateFunctionJson.ExtractStateName(stateBody) + ); + Assert.True( + StateFunctionJson.TransitionsContainKey(stateBody, "complete-grandchild"), + "State function should expose complete-grandchild while grandchild-initial is the effective state." + ); + } + + // TODO / BUG: Child workflow defines updateData (update-parent-data) as required for SubFlow context, + // but PATCH through the parent instance currently resolves only the parent workflow and returns + // Instance:100024 ("subflow-orchestration-parent does not define an updateData configuration"). + // Workflow definition is assumed correct per vnext-workflow-creation §13.2; platform/runtime needs investigation. + [Fact] + public async Task UpdateData_FromChildSubflow_UpdatesParentData() + { + var instanceId = await _scenario.StartAsync("subflow-orch-update-data", "update-data"); + await _scenario.ProceedToGrandchildAsync(instanceId); + + await _scenario.RunUpdateParentDataAsync(instanceId, "from-child-updatedata"); + + var attributes = await _wf.GetAttributesAsync(instanceId); + SubflowOrchestrationInstanceDataAssertions.AssertUpdateDataApplied(attributes); + + await _scenario.CompleteGrandchildAsync(instanceId); + } + + [Fact] + public async Task CancelParent_WhileGrandchildActive_ReachesParentCancelled() + { + var instanceId = await _scenario.StartAsync( + "subflow-orch-cancel-cascade", + "cancel-cascade" + ); + await _scenario.ProceedToGrandchildAsync(instanceId); + + await _scenario.CancelParentAsync(instanceId); + + var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); + Assert.Equal( + SubflowOrchestrationScenarioActions.ParentCancelledState, + StateFunctionJson.ExtractStateName(stateBody) + ); + Assert.Equal("C", StateFunctionJson.ExtractStatus(stateBody)); + } +} diff --git a/vnext-example.sln.DotSettings.user b/vnext-example.sln.DotSettings.user new file mode 100644 index 0000000..9cd4e63 --- /dev/null +++ b/vnext-example.sln.DotSettings.user @@ -0,0 +1,7 @@ + + <SessionState ContinuousTestingMode="0" Name="UpdateData_FromChildSubflow_UpdatesParentData" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <TestAncestor> + <TestId>xUnit::E4963139-E4EB-2D4C-E609-219AB4596A8A::net10.0::Core.IntegrationTests.Tests.SubflowOrchestrationTestWorkflow.SubflowOrchestrationTestWorkflowTests.UpdateData_FromChildSubflow_UpdatesParentData</TestId> + </TestAncestor> +</SessionState> + \ No newline at end of file From 3a0a90330b032f9d02206aa3d5944a690e75b31b Mon Sep 17 00:00:00 2001 From: Eren Kara Date: Tue, 12 May 2026 14:14:26 +0300 Subject: [PATCH 24/24] domain scoped function test bug resolved. --- core.link.json | 2 +- .../error-boundary-test-workflow.json | 18 +- .../lifecycle-transitions-test-workflow.json | 1 + .../subflow-orchestration-child.diagram.json | 4 + .../subflow-orchestration-child.json | 582 ++++++++---------- .../subflow-orchestration-parent.json | 6 +- tests/Core/Helpers/DomainFunctionHelper.cs | 33 - .../Helpers/WorkflowInstanceTestHelper.cs | 19 - .../ViewFunctionExtensionTestWorkflowTests.cs | 88 +-- vnext-example.sln.DotSettings.user | 7 - 10 files changed, 339 insertions(+), 421 deletions(-) delete mode 100644 tests/Core/Helpers/DomainFunctionHelper.cs delete mode 100644 vnext-example.sln.DotSettings.user diff --git a/core.link.json b/core.link.json index b277919..ac6298a 100644 --- a/core.link.json +++ b/core.link.json @@ -1,5 +1,5 @@ { "sourcePath": "c:/CalismaAlani/Burgan/test_real/vnext-example", "domain": "core", - "importedAt": "2026-05-11T14:35:14.679Z" + "importedAt": "2026-05-12T09:23:57.746Z" } \ No newline at end of file diff --git a/core/Workflows/error-boundary/error-boundary-test-workflow.json b/core/Workflows/error-boundary/error-boundary-test-workflow.json index bf3e310..d7fb77d 100644 --- a/core/Workflows/error-boundary/error-boundary-test-workflow.json +++ b/core/Workflows/error-boundary/error-boundary-test-workflow.json @@ -28,9 +28,7 @@ "onError": [ { "action": 0, - "errorCodes": [ - "*" - ], + "errorCodes": ["*"], "priority": 999 } ] @@ -139,9 +137,7 @@ }, { "action": 3, - "errorCodes": [ - "*" - ], + "errorCodes": ["*"], "priority": 999 } ] @@ -353,16 +349,12 @@ "onError": [ { "action": 3, - "errorTypes": [ - "InvalidOperationException" - ], + "errorTypes": ["InvalidOperationException"], "priority": 1 }, { "action": 5, - "errorTypes": [ - "*" - ], + "errorTypes": ["*"], "priority": 999 } ] @@ -647,4 +639,4 @@ } ] } -} \ No newline at end of file +} diff --git a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json index 63b9ea9..78a5da2 100644 --- a/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json +++ b/core/Workflows/lifecycle-transitions/lifecycle-transitions-test-workflow.json @@ -52,6 +52,7 @@ "target": "terminated-state", "triggerType": 0, "versionStrategy": "Major", + "availableIn": [], "labels": [ { "language": "en-US", diff --git a/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-child.diagram.json b/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-child.diagram.json index 609bb4a..3eb6faf 100644 --- a/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-child.diagram.json +++ b/core/Workflows/subflow-orchestration/.meta/subflow-orchestration-child.diagram.json @@ -31,6 +31,10 @@ "__wf_shared_shared-child-update__": { "x": -370, "y": 520 + }, + "child-manual-state": { + "x": -380, + "y": 490 } } } \ No newline at end of file diff --git a/core/Workflows/subflow-orchestration/subflow-orchestration-child.json b/core/Workflows/subflow-orchestration/subflow-orchestration-child.json index 96c055d..bb6cdeb 100644 --- a/core/Workflows/subflow-orchestration/subflow-orchestration-child.json +++ b/core/Workflows/subflow-orchestration/subflow-orchestration-child.json @@ -1,310 +1,274 @@ { - "key": "subflow-orchestration-child", - "flow": "sys-flows", - "flowVersion": "1.0.0", - "domain": "core", - "version": "1.0.0", - "tags": [ - "integration-test", - "subflow-orchestration", - "child", - "subflow", - "shared-transitions" - ], - "attributes": { - "type": "S", - "timeout": null, - "labels": [ - { - "language": "en-US", - "label": "SubFlow Orchestration Child" - } - ], - "functions": [ - - ], - "features": [ - - ], - "extensions": [ - - ], - "sharedTransitions": [ - { - "key": "shared-child-mark", - "target": "$self", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Shared Child Mark ($self)" - } - ], - "availableIn": [ - "child-manual-state" - ], - "onExecutionTasks": [ - { - "order": 1, - "task": { - "key": "subflow-script-task", - "domain": "core", - "version": "1.0.0", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/ChildSharedMarkMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIENoaWxkU2hhcmVkTWFya01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNoaWxkU3RhcnRlZCIpKSByZXN1bHQuY2hpbGRTdGFydGVkID0gZGF0YS5jaGlsZFN0YXJ0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJwYXJlbnREYXRhIikpIHJlc3VsdC5wYXJlbnREYXRhID0gZGF0YS5wYXJlbnREYXRhOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICByZXN1bHQuY2hpbGRTaGFyZWRNYXJrRXhlY3V0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC5jaGlsZFNoYXJlZE1hcmtBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwogICAgICAgIExvZ0luZm9ybWF0aW9uKCJDaGlsZFNoYXJlZE1hcmtNYXBwaW5nIGNvbXBsZXRlZCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" - } - } - ] - } - ], - "cancel": { - "key": "cancel-child", - "target": "child-cancelled", - "triggerType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Cancel Child" - } - ] - }, - "updateData": { - "key": "update-parent-data", - "target": "$self", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Update Parent Data from Child" - } - ], - "availableIn": [ - - ], - "onExecutionTasks": [ - { - "order": 1, - "task": { - "key": "subflow-script-task", - "domain": "core", - "version": "1.0.0", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/UpdateParentDataMapping.csx", - "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFVwZGF0ZVBhcmVudERhdGFNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY2hpbGRTdGFydGVkIikpCiAgICAgICAgICAgIHJlc3VsdC5jaGlsZFN0YXJ0ZWQgPSBkYXRhLmNoaWxkU3RhcnRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdyYW5kY2hpbGRGaW5pc2hlZCIpKQogICAgICAgICAgICByZXN1bHQuZ3JhbmRjaGlsZEZpbmlzaGVkID0gZGF0YS5ncmFuZGNoaWxkRmluaXNoZWQ7CgogICAgICAgIHJlc3VsdC5jaGlsZFVwZGF0ZWRQYXJlbnQgPSB0cnVlOwogICAgICAgIHJlc3VsdC51cGRhdGVQYXJlbnRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiVXBkYXRlUGFyZW50RGF0YU1hcHBpbmcgY29tcGxldGVkIC0gdXBkYXRlRGF0YSBmcm9tIGNoaWxkIHRvIHBhcmVudCIpOwoKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" - } - } - ] - }, - "startTransition": { - "key": "start-subflow-orchestration-child", - "target": "child-initial", - "triggerType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Start Child" - } - ] - }, - "states": [ - { - "key": "child-initial", - "stateType": 1, - "subType": 0, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Child Initial" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - { - "order": 1, - "task": { - "key": "subflow-script-task", - "domain": "core", - "version": "1.0.0", - "flow": "sys-tasks" - }, - "mapping": { - "location": "./src/ChildStartMapping.csx", - "code": "dXNpbmcgU3lzdGVtLkR5bmFtaWM7CnVzaW5nIFN5c3RlbS5UaHJlYWRpbmcuVGFza3M7CnVzaW5nIEJCVC5Xb3JrZmxvdy5EZWZpbml0aW9uczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBDaGlsZFN0YXJ0TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC5jaGlsZFN0YXJ0ZWQgPSB0cnVlOwogICAgICAgIGlmIChkYXRhICE9IG51bGwpCiAgICAgICAgewogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInBhcmVudERhdGEiKSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcmVzdWx0LnBhcmVudERhdGEgPSBkYXRhLnBhcmVudERhdGE7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIExvZ0luZm9ybWF0aW9uKCJDaGlsZFN0YXJ0TWFwcGluZzogY2hpbGRTdGFydGVkIHNldCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" - } - } - ], - "onExits": [ - - ], - "transitions": [ - { - "key": "auto-child-to-manual", - "target": "child-manual-state", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Auto to Child Manual State" - } - ], - "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" - }, - "onExecutionTasks": [ - - ] - } - ] - }, - { - "key": "child-manual-state", - "stateType": 2, - "subType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Child Manual State" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - - ], - "onExits": [ - - ], - "transitions": [ - { - "key": "proceed-to-subflow", - "target": "child-subflow-state", - "triggerType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Proceed to SubFlow" - } - ], - "onExecutionTasks": [ - - ] - } - ] - }, - { - "key": "child-subflow-state", - "stateType": 4, - "subType": 0, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Child SubFlow State" - } - ], - "view": null, - "subFlow": { - "type": "S", - "process": { - "key": "subflow-orchestration-grandchild", - "domain": "core", - "version": "1.0.0", - "flow": "sys-flows" - }, - "mapping": { - "location": "./src/ChildToGrandchildSubFlowMapping.csx", - "code": "dXNpbmcgU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWM7CnVzaW5nIFN5c3RlbS5EeW5hbWljOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIENoaWxkVG9HcmFuZGNoaWxkU3ViRmxvd01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJU3ViRmxvd01hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyBnY0lucHV0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoZGF0YSAhPSBudWxsKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJwYXJlbnREYXRhIikpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGdjSW5wdXQucGFyZW50RGF0YSA9IGRhdGEucGFyZW50RGF0YTsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBnY0lucHV0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY2hpbGRTdGFydGVkIikpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGdjSW5wdXQuY2hpbGRTdGFydGVkID0gZGF0YS5jaGlsZFN0YXJ0ZWQ7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkNoaWxkVG9HcmFuZGNoaWxkU3ViRmxvd01hcHBpbmc6IHByZXBhcmVkIGdyYW5kY2hpbGQgaW5wdXQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSBnY0lucHV0IH0pOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgYm9keSA9IGNvbnRleHQuQm9keSBhcyBJRGljdGlvbmFyeTxzdHJpbmcsIG9iamVjdD47CiAgICAgICAgZHluYW1pYyBtZXJnZWQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHZhciB0YXJnZXQgPSAoSURpY3Rpb25hcnk8c3RyaW5nLCBvYmplY3Q+KW1lcmdlZDsKICAgICAgICB2YXIgaW5zdCA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YSBhcyBJRGljdGlvbmFyeTxzdHJpbmcsIG9iamVjdD47CiAgICAgICAgaWYgKGluc3QgIT0gbnVsbCkKICAgICAgICB7CiAgICAgICAgICAgIGZvcmVhY2ggKHZhciBrdiBpbiBpbnN0KQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICB0YXJnZXRba3YuS2V5XSA9IGt2LlZhbHVlOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIGlmIChib2R5ICE9IG51bGwpCiAgICAgICAgewogICAgICAgICAgICBmb3JlYWNoICh2YXIga3YgaW4gYm9keSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgdGFyZ2V0W2t2LktleV0gPSBrdi5WYWx1ZTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICB0YXJnZXRbImdyYW5kY2hpbGRDb21wbGV0ZWQiXSA9IHRydWU7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkNoaWxkVG9HcmFuZGNoaWxkU3ViRmxvd01hcHBpbmc6IG1lcmdlZCBncmFuZGNoaWxkIG91dHB1dCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IG1lcmdlZCB9KTsKICAgIH0KfQo=" - } - }, - "onEntries": [ - - ], - "onExits": [ - - ], - "transitions": [ - { - "key": "auto-child-to-completed", - "target": "child-completed", - "triggerType": 1, - "versionStrategy": "Minor", - "labels": [ - { - "language": "en-US", - "label": "Auto to Child Completed" - } - ], - "rule": { - "location": "./src/AlwaysTrueRule.csx", - "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" - }, - "onExecutionTasks": [ - - ] - } - ] - }, - { - "key": "child-completed", - "stateType": 3, - "subType": 1, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Child Completed" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - - ], - "onExits": [ - - ], - "transitions": [ - - ] - }, - { - "key": "child-cancelled", - "stateType": 3, - "subType": 3, - "versionStrategy": "Major", - "labels": [ - { - "language": "en-US", - "label": "Child Cancelled" - } - ], - "view": null, - "subFlow": null, - "onEntries": [ - - ], - "onExits": [ - - ], - "transitions": [ - - ] - } - ] - } -} + "key": "subflow-orchestration-child", + "flow": "sys-flows", + "flowVersion": "1.0.0", + "domain": "core", + "version": "1.0.0", + "tags": [ + "integration-test", + "subflow-orchestration", + "child", + "subflow", + "shared-transitions" + ], + "attributes": { + "type": "S", + "timeout": null, + "labels": [ + { + "language": "en-US", + "label": "SubFlow Orchestration Child" + } + ], + "functions": [], + "features": [], + "extensions": [], + "sharedTransitions": [ + { + "key": "shared-child-mark", + "target": "$self", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Shared Child Mark ($self)" + } + ], + "availableIn": [ + "child-manual-state" + ], + "onExecutionTasks": [ + { + "order": 1, + "task": { + "key": "subflow-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/ChildSharedMarkMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIENoaWxkU2hhcmVkTWFya01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHZhciBkYXRhID0gY29udGV4dC5JbnN0YW5jZS5EYXRhOwogICAgICAgIGR5bmFtaWMgcmVzdWx0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImNoaWxkU3RhcnRlZCIpKSByZXN1bHQuY2hpbGRTdGFydGVkID0gZGF0YS5jaGlsZFN0YXJ0ZWQ7CiAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJwYXJlbnREYXRhIikpIHJlc3VsdC5wYXJlbnREYXRhID0gZGF0YS5wYXJlbnREYXRhOwogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAidGVzdElkIikpIHJlc3VsdC50ZXN0SWQgPSBkYXRhLnRlc3RJZDsKICAgICAgICByZXN1bHQuY2hpbGRTaGFyZWRNYXJrRXhlY3V0ZWQgPSB0cnVlOwogICAgICAgIHJlc3VsdC5jaGlsZFNoYXJlZE1hcmtBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwogICAgICAgIExvZ0luZm9ybWF0aW9uKCJDaGlsZFNoYXJlZE1hcmtNYXBwaW5nIGNvbXBsZXRlZCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" + } + } + ] + } + ], + "cancel": { + "key": "cancel-child", + "target": "child-cancelled", + "triggerType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Cancel Child" + } + ] + }, + "updateData": { + "key": "update-parent-data", + "target": "$self", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Update Parent Data from Child" + } + ], + "availableIn": [], + "onExecutionTasks": [ + { + "order": 1, + "task": { + "key": "subflow-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/UpdateParentDataMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uRHluYW1pYzsKdXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LkRlZmluaXRpb25zOwoKcHVibGljIGNsYXNzIFVwZGF0ZVBhcmVudERhdGFNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgZGF0YSA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YTsKICAgICAgICBkeW5hbWljIHJlc3VsdCA9IG5ldyBFeHBhbmRvT2JqZWN0KCk7CgogICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY2hpbGRTdGFydGVkIikpCiAgICAgICAgICAgIHJlc3VsdC5jaGlsZFN0YXJ0ZWQgPSBkYXRhLmNoaWxkU3RhcnRlZDsKICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgImdyYW5kY2hpbGRGaW5pc2hlZCIpKQogICAgICAgICAgICByZXN1bHQuZ3JhbmRjaGlsZEZpbmlzaGVkID0gZGF0YS5ncmFuZGNoaWxkRmluaXNoZWQ7CgogICAgICAgIHJlc3VsdC5jaGlsZFVwZGF0ZWRQYXJlbnQgPSB0cnVlOwogICAgICAgIHJlc3VsdC51cGRhdGVQYXJlbnRBdCA9IERhdGVUaW1lLlV0Y05vdy5Ub1N0cmluZygibyIpOwoKICAgICAgICBMb2dJbmZvcm1hdGlvbigiVXBkYXRlUGFyZW50RGF0YU1hcHBpbmcgY29tcGxldGVkIC0gdXBkYXRlRGF0YSBmcm9tIGNoaWxkIHRvIHBhcmVudCIpOwoKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSByZXN1bHQgfSk7CiAgICB9Cn0K" + } + } + ] + }, + "startTransition": { + "key": "start-subflow-orchestration-child", + "target": "child-initial", + "triggerType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Start Child" + } + ] + }, + "states": [ + { + "key": "child-initial", + "stateType": 1, + "subType": 0, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Child Initial" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "subflow-script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/ChildStartMapping.csx", + "code": "dXNpbmcgU3lzdGVtLkR5bmFtaWM7CnVzaW5nIFN5c3RlbS5UaHJlYWRpbmcuVGFza3M7CnVzaW5nIEJCVC5Xb3JrZmxvdy5EZWZpbml0aW9uczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBDaGlsZFN0YXJ0TWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSgpKTsKICAgIH0KCiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyByZXN1bHQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHJlc3VsdC5jaGlsZFN0YXJ0ZWQgPSB0cnVlOwogICAgICAgIGlmIChkYXRhICE9IG51bGwpCiAgICAgICAgewogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInBhcmVudERhdGEiKSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcmVzdWx0LnBhcmVudERhdGEgPSBkYXRhLnBhcmVudERhdGE7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJ0ZXN0SWQiKSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcmVzdWx0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIExvZ0luZm9ybWF0aW9uKCJDaGlsZFN0YXJ0TWFwcGluZzogY2hpbGRTdGFydGVkIHNldCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IHJlc3VsdCB9KTsKICAgIH0KfQo=" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "auto-child-to-manual", + "target": "child-manual-state", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to Child Manual State" + } + ], + "rule": { + "location": "./src/AlwaysTrueRule.csx", + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" + }, + "onExecutionTasks": [] + } + ] + }, + { + "key": "child-manual-state", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Child Manual State" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "proceed-to-subflow", + "target": "child-subflow-state", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Proceed to SubFlow" + } + ], + "onExecutionTasks": [] + } + ] + }, + { + "key": "child-subflow-state", + "stateType": 4, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Child SubFlow State" + } + ], + "view": null, + "subFlow": { + "type": "S", + "process": { + "key": "subflow-orchestration-grandchild", + "domain": "core", + "version": "1.0.0", + "flow": "sys-flows" + }, + "mapping": { + "location": "./src/ChildToGrandchildSubFlowMapping.csx", + "code": "dXNpbmcgU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWM7CnVzaW5nIFN5c3RlbS5EeW5hbWljOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwoKcHVibGljIGNsYXNzIENoaWxkVG9HcmFuZGNoaWxkU3ViRmxvd01hcHBpbmcgOiBTY3JpcHRCYXNlLCBJU3ViRmxvd01hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdmFyIGRhdGEgPSBjb250ZXh0Lkluc3RhbmNlLkRhdGE7CiAgICAgICAgZHluYW1pYyBnY0lucHV0ID0gbmV3IEV4cGFuZG9PYmplY3QoKTsKICAgICAgICBpZiAoZGF0YSAhPSBudWxsKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKEhhc1Byb3BlcnR5KGRhdGEsICJwYXJlbnREYXRhIikpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGdjSW5wdXQucGFyZW50RGF0YSA9IGRhdGEucGFyZW50RGF0YTsKICAgICAgICAgICAgfQogICAgICAgICAgICBpZiAoSGFzUHJvcGVydHkoZGF0YSwgInRlc3RJZCIpKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBnY0lucHV0LnRlc3RJZCA9IGRhdGEudGVzdElkOwogICAgICAgICAgICB9CiAgICAgICAgICAgIGlmIChIYXNQcm9wZXJ0eShkYXRhLCAiY2hpbGRTdGFydGVkIikpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGdjSW5wdXQuY2hpbGRTdGFydGVkID0gZGF0YS5jaGlsZFN0YXJ0ZWQ7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkNoaWxkVG9HcmFuZGNoaWxkU3ViRmxvd01hcHBpbmc6IHByZXBhcmVkIGdyYW5kY2hpbGQgaW5wdXQiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZSB7IERhdGEgPSBnY0lucHV0IH0pOwogICAgfQoKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB2YXIgYm9keSA9IGNvbnRleHQuQm9keSBhcyBJRGljdGlvbmFyeTxzdHJpbmcsIG9iamVjdD47CiAgICAgICAgZHluYW1pYyBtZXJnZWQgPSBuZXcgRXhwYW5kb09iamVjdCgpOwogICAgICAgIHZhciB0YXJnZXQgPSAoSURpY3Rpb25hcnk8c3RyaW5nLCBvYmplY3Q+KW1lcmdlZDsKICAgICAgICB2YXIgaW5zdCA9IGNvbnRleHQuSW5zdGFuY2UuRGF0YSBhcyBJRGljdGlvbmFyeTxzdHJpbmcsIG9iamVjdD47CiAgICAgICAgaWYgKGluc3QgIT0gbnVsbCkKICAgICAgICB7CiAgICAgICAgICAgIGZvcmVhY2ggKHZhciBrdiBpbiBpbnN0KQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICB0YXJnZXRba3YuS2V5XSA9IGt2LlZhbHVlOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIGlmIChib2R5ICE9IG51bGwpCiAgICAgICAgewogICAgICAgICAgICBmb3JlYWNoICh2YXIga3YgaW4gYm9keSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgdGFyZ2V0W2t2LktleV0gPSBrdi5WYWx1ZTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICB0YXJnZXRbImdyYW5kY2hpbGRDb21wbGV0ZWQiXSA9IHRydWU7CiAgICAgICAgTG9nSW5mb3JtYXRpb24oIkNoaWxkVG9HcmFuZGNoaWxkU3ViRmxvd01hcHBpbmc6IG1lcmdlZCBncmFuZGNoaWxkIG91dHB1dCIpOwogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlIHsgRGF0YSA9IG1lcmdlZCB9KTsKICAgIH0KfQo=" + } + }, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "auto-child-to-completed", + "target": "child-completed", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Auto to Child Completed" + } + ], + "rule": { + "location": "./src/AlwaysTrueRule.csx", + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IFNjcmlwdEJhc2UsIElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIExvZ0luZm9ybWF0aW9uKCJBbHdheXNUcnVlUnVsZTogcmV0dXJuaW5nIHRydWUiKTsKICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KHRydWUpOwogICAgfQp9Cg==" + }, + "onExecutionTasks": [] + } + ] + }, + { + "key": "child-completed", + "stateType": 3, + "subType": 1, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Child Completed" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [] + }, + { + "key": "child-cancelled", + "stateType": 3, + "subType": 3, + "versionStrategy": "Major", + "labels": [ + { + "language": "en-US", + "label": "Child Cancelled" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [] + } + ] + } +} \ No newline at end of file diff --git a/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json b/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json index 13caa32..6504162 100644 --- a/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json +++ b/core/Workflows/subflow-orchestration/subflow-orchestration-parent.json @@ -35,9 +35,7 @@ "label": "Shared Common Transition" } ], - "availableIn": [ - "parent-subflow-state" - ], + "availableIn": ["parent-subflow-state"], "onExecutionTasks": [ { "order": 1, @@ -252,4 +250,4 @@ } ] } -} \ No newline at end of file +} diff --git a/tests/Core/Helpers/DomainFunctionHelper.cs b/tests/Core/Helpers/DomainFunctionHelper.cs deleted file mode 100644 index 173506c..0000000 --- a/tests/Core/Helpers/DomainFunctionHelper.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Net; -using System.Text.Json; -using VNext.Testing.Sdk.Client; -using Xunit; - -namespace Core.IntegrationTests.Helpers; - -/// -/// Helpers for domain-scoped function calls (GET .../functions/{functionName}) -/// that are NOT tied to a specific workflow key. -/// -public static class DomainFunctionHelper -{ - public static async Task CallDomainScopeFunctionAsync( - VNextApiClient api, - string domain, - string apiVersion, - string functionName, - Dictionary? queryParams = null - ) - { - var qs = queryParams != null - ? string.Join("&", queryParams.Select(kv => $"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value)}")) - : ""; - var path = $"/api/v{apiVersion}/{domain}/functions/{Uri.EscapeDataString(functionName)}"; - if (!string.IsNullOrEmpty(qs)) - path += "?" + qs; - - var response = await api.GetRawAsync(path); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - return response.Body; - } -} diff --git a/tests/Core/Helpers/WorkflowInstanceTestHelper.cs b/tests/Core/Helpers/WorkflowInstanceTestHelper.cs index fc2e2fa..7e9194f 100644 --- a/tests/Core/Helpers/WorkflowInstanceTestHelper.cs +++ b/tests/Core/Helpers/WorkflowInstanceTestHelper.cs @@ -212,25 +212,6 @@ public async Task CallFunctionAsync( return response.Body; } - /// - /// Calls a workflow-scoped function (GET .../workflows/{workflowKey}/functions/{functionName}). - /// - public async Task CallWorkflowScopeFunctionAsync( - string functionName, - Dictionary? queryParams = null, - Dictionary? headers = null - ) - { - var response = await _api.CallWorkflowFunctionAsync( - _workflowKey, - functionName, - queryParams, - headers - ); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - return response.Body; - } - /// /// Calls GET .../instances with any subset of filter / sort / page / /// pageSize query parameters and asserts HTTP 200 before returning the response body. diff --git a/tests/Core/Tests/view-function-extension-test-workflow/ViewFunctionExtensionTestWorkflowTests.cs b/tests/Core/Tests/view-function-extension-test-workflow/ViewFunctionExtensionTestWorkflowTests.cs index c9a0233..5e5189c 100644 --- a/tests/Core/Tests/view-function-extension-test-workflow/ViewFunctionExtensionTestWorkflowTests.cs +++ b/tests/Core/Tests/view-function-extension-test-workflow/ViewFunctionExtensionTestWorkflowTests.cs @@ -1,3 +1,4 @@ +using System.Net; using System.Text.Json; using Core.IntegrationTests.Helpers; using Core.IntegrationTests.Infrastructure; @@ -5,9 +6,7 @@ namespace Core.IntegrationTests.Tests.ViewFunctionExtensionTestWorkflow; /* -! Bugs: -! Workflow-scope (F) function — GET /workflows/{key}/functions/{name} returns 404 (instance-scope I works) -! Domain-scope (D) function — GET /api/v1/{domain}/functions/{name} returns empty/null body +! Known gap: workflow-scope (F) function — GET /workflows/{key}/functions/{name} may return 404 on some runtimes. */ /// @@ -158,8 +157,8 @@ public async Task UrnViewState_Type6_Inline_HasView() // The view IS attached to the state (confirmed by state response above). Assert.Fail( "Runtime returned 404 for functions/view on URN (type 6) view. " - + "State confirms view.hasView=true but content endpoint fails. " - + $"Exception: {ex.Message}" + + "State confirms view.hasView=true but content endpoint fails. " + + $"Exception: {ex.Message}" ); } } @@ -192,8 +191,10 @@ public async Task WizardStepState_SingleTransition_TransitionViewRendered() var stateBody = await _wf.GetStateFunctionBodyAsync(instanceId, headers: null); Assert.Equal("wizard-step-state", StateFunctionJson.ExtractStateName(stateBody)); - if (stateBody.TryGetProperty("transitions", out var transitions) - && transitions.ValueKind == JsonValueKind.Array) + if ( + stateBody.TryGetProperty("transitions", out var transitions) + && transitions.ValueKind == JsonValueKind.Array + ) { Assert.True( transitions.GetArrayLength() == 1, @@ -212,7 +213,7 @@ public async Task WizardStepState_SingleTransition_TransitionViewRendered() hasView.ValueKind == JsonValueKind.False || hasView.ValueKind == JsonValueKind.Null, "Wizard state should not render a state-level view (hasView should be false/null). " - + "Wizard states render the transition view instead." + + "Wizard states render the transition view instead." ); } } @@ -260,7 +261,7 @@ public async Task GlobalExtension_Type1_AppliesImplicitly_WithoutWorkflowReferen // when the extension is not referenced in the workflow's extensions array. Assert.Fail( "Extension 'vfe-global-extension' (Type 1, Global, Scope 3) not found in functions/data response. " - + $"Actual functions/data keys: {dataSummary}" + + $"Actual functions/data keys: {dataSummary}" ); } } @@ -330,7 +331,7 @@ public async Task GlobalAndRequestedExtension_Type2_AutoAppliesAndQueryable() var dataSummary = SummarizeJson(dataBody, 300); Assert.Fail( "Extension 'vfe-global-and-requested-extension' (Type 2, Scope 1) not found in GetInstance or functions/data. " - + $"GetInstance keys: {instSummary} | functions/data keys: {dataSummary}" + + $"GetInstance keys: {instSummary} | functions/data keys: {dataSummary}" ); } @@ -353,7 +354,10 @@ public async Task DefinedFlowAndRequestedExtension_Type4_OnlyAppearsWhenRequeste var dataBody = await _wf.CallFunctionAsync(instanceId, "data"); - bool presentByDefault = TryHasExtension(dataBody, "vfe-defined-flow-and-requested-extension"); + bool presentByDefault = TryHasExtension( + dataBody, + "vfe-defined-flow-and-requested-extension" + ); if (presentByDefault) { Assert.Fail( @@ -383,7 +387,7 @@ public async Task DefinedFlowAndRequestedExtension_Type4_OnlyAppearsWhenRequeste // TODO: Runtime did not return Type 4 extension even with ?extensions= query on functions/data. Assert.Fail( "Extension 'vfe-defined-flow-and-requested-extension' (Type 4) not found even with " - + "?extensions= query on functions/data endpoint." + + "?extensions= query on functions/data endpoint." ); } } @@ -445,7 +449,7 @@ public async Task SingleTaskFunction_InstanceScope_ReturnsResponse() Assert.True( VfeFunctionAssertions.TryAssertFunctionPropertyTrue(body, "singleTaskFunction"), $"Function 'vfe-single-task-function' response.singleTaskFunction should be true. " - + $"Actual body: {bodySummary}" + + $"Actual body: {bodySummary}" ); } @@ -461,7 +465,7 @@ public async Task MultiTaskFunction_InstanceScope_AggregatesResponses() Assert.True( VfeFunctionAssertions.TryAssertFunctionPropertyTrue(body, "aggregated"), $"Function 'vfe-multi-task-function' response.aggregated should be true. " - + $"Actual body: {bodySummary}" + + $"Actual body: {bodySummary}" ); } @@ -470,7 +474,14 @@ public async Task WorkflowFunction_WorkflowScope_ReturnsResponse() { try { - var body = await _wf.CallWorkflowScopeFunctionAsync("vfe-workflow-function"); + var response = await Api.CallWorkflowFunctionAsync( + WorkflowKey, + "vfe-workflow-function", + queryParams: null, + headers: null + ); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = response.Body; VfeFunctionAssertions.AssertFunctionResponseNotEmpty(body, "vfe-workflow-function"); VfeFunctionAssertions.AssertFunctionProperty( body, @@ -483,8 +494,8 @@ public async Task WorkflowFunction_WorkflowScope_ReturnsResponse() { // TODO: Runtime returns 404 for workflow-scope (F) function endpoint. Assert.Fail( - "Workflow-scope function 'vfe-workflow-function' returned 404. " - + "Runtime may not support scope 'F' functions via workflow-level endpoint." + $"Workflow-scope function 'vfe-workflow-function' {ex.Message}" + + "Runtime may not support scope 'F' functions via workflow-level endpoint." ); } } @@ -494,19 +505,19 @@ public async Task DomainFunction_DomainScope_ReturnsResponse() { try { - var body = await DomainFunctionHelper.CallDomainScopeFunctionAsync( - Api, - "core", - "1", - "vfe-domain-function" + var response = await Api.CallFunctionAsync( + "vfe-domain-function", + queryParams: null, + headers: null ); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = response.Body; - if (body.ValueKind == JsonValueKind.Undefined - || body.ValueKind == JsonValueKind.Null) + if (body.ValueKind == JsonValueKind.Undefined || body.ValueKind == JsonValueKind.Null) { Assert.Fail( "Domain-scope function 'vfe-domain-function' returned empty response. " - + "Runtime may not support scope 'D' functions via domain-level endpoint." + + "Runtime may not support scope 'D' functions via domain-level endpoint." ); return; } @@ -523,7 +534,7 @@ public async Task DomainFunction_DomainScope_ReturnsResponse() { Assert.Fail( $"Domain-scope function 'vfe-domain-function' failed: {ex.Message}. " - + "Runtime may not support scope 'D' functions." + + "Runtime may not support scope 'D' functions." ); } } @@ -536,18 +547,24 @@ private static bool TryHasExtension(JsonElement body, string extensionKey) { var camelKey = ToCamelCase(extensionKey); - if (body.ValueKind == JsonValueKind.Object + if ( + body.ValueKind == JsonValueKind.Object && body.TryGetProperty("extensions", out var ext) && ext.ValueKind == JsonValueKind.Object - && (ext.TryGetProperty(extensionKey, out _) || ext.TryGetProperty(camelKey, out _))) + && (ext.TryGetProperty(extensionKey, out _) || ext.TryGetProperty(camelKey, out _)) + ) return true; - if (body.ValueKind == JsonValueKind.Object + if ( + body.ValueKind == JsonValueKind.Object && body.TryGetProperty("data", out var data) && data.ValueKind == JsonValueKind.Object && data.TryGetProperty("extensions", out var nested) && nested.ValueKind == JsonValueKind.Object - && (nested.TryGetProperty(extensionKey, out _) || nested.TryGetProperty(camelKey, out _))) + && ( + nested.TryGetProperty(extensionKey, out _) || nested.TryGetProperty(camelKey, out _) + ) + ) return true; return false; @@ -556,11 +573,12 @@ private static bool TryHasExtension(JsonElement body, string extensionKey) private static string ToCamelCase(string kebab) { var parts = kebab.Split('-'); - if (parts.Length <= 1) return kebab; - return parts[0] + string.Concat( - parts.Skip(1).Select(p => - p.Length == 0 ? p : char.ToUpperInvariant(p[0]) + p[1..]) - ); + if (parts.Length <= 1) + return kebab; + return parts[0] + + string.Concat( + parts.Skip(1).Select(p => p.Length == 0 ? p : char.ToUpperInvariant(p[0]) + p[1..]) + ); } private static string SummarizeJson(JsonElement el, int maxLen) diff --git a/vnext-example.sln.DotSettings.user b/vnext-example.sln.DotSettings.user deleted file mode 100644 index 9cd4e63..0000000 --- a/vnext-example.sln.DotSettings.user +++ /dev/null @@ -1,7 +0,0 @@ - - <SessionState ContinuousTestingMode="0" Name="UpdateData_FromChildSubflow_UpdatesParentData" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <TestAncestor> - <TestId>xUnit::E4963139-E4EB-2D4C-E609-219AB4596A8A::net10.0::Core.IntegrationTests.Tests.SubflowOrchestrationTestWorkflow.SubflowOrchestrationTestWorkflowTests.UpdateData_FromChildSubflow_UpdatesParentData</TestId> - </TestAncestor> -</SessionState> - \ No newline at end of file