[
  {
    "path": ".gitar/rules/pr-description-quality.md",
    "content": "---\ntitle: PR Description Quality Standards\ndescription: Ensures PR descriptions meet quality criteria for the cadence-samples (Go) repo using guidance from the PR template and .github/pull_request_guidance.md\nwhen: PR description is created or updated\nactions: Read PR template and guidance, then report requirement status\n---\n\n# PR Description Quality Standards\n\nWhen evaluating a pull request description:\n\n1. **Read the PR template guidance** at `.github/pull_request_guidance.md` to understand the expected guidance for each section\n2. Apply that guidance to evaluate the current PR description\n3. Provide recommendations for how to improve the description.\n\n## Core Principle: Why Not How\n\nFrom https://cbea.ms/git-commit/#why-not-how:\n- **\"A diff shows WHAT changed, but only the description can explain WHY\"**\n- Focus on: the problem being solved, the reasoning behind the solution, context\n- The code itself documents HOW - the PR description documents WHY\n\n## Evaluation Criteria\n\n### Required Sections (must exist with substantive content per PR template guidance)\n\n1. **Which sample(s) or area?** \n   - One line listing area(s) touched. This repo has two sample trees: **cmd/samples** (legacy; make + ./bin/…) and **new_samples** (per-folder; go run .). Identify which tree and area(s) are touched.\n   - **cmd/samples:** e.g. cmd/samples/recipes/helloworld, cmd/samples/recipes/branch, cmd/samples/recipes/query, cmd/samples/batch, cmd/samples/cron, cmd/samples/expense, cmd/samples/fileprocessing, cmd/samples/dsl, cmd/samples/pso, cmd/samples/recovery, cmd/samples/recipes/cancelactivity, etc.\n   - **new_samples:** hello_world, activities, query, signal, operations, client_tls, template.\n   - **Other:** README, build, config, Makefile, common.\n   - Helps reviewers route; skip flagging if area is obvious from paths\n\n2. **What changed?**\n   - 1-2 line summary of WHAT changed technically\n   - Focus on key modification, not implementation details\n   - Link to GitHub issue encouraged for non-trivial changes; optional for trivial doc/sample tweaks\n   - Template has good/bad examples\n\n3. **Why?**\n   - Context and motivation (why not how)\n   - Enough rationale for reviewers to understand the goal (e.g. improving clarity, fixing compatibility, aligning with docs)\n   - Must explain WHY this approach was chosen\n\n4. **How did you test it?**\n   - Concrete, copyable commands with exact invocations\n   - GOOD: `make`, `go test ./...`, `go test ./cmd/samples/recipes/helloworld/`, `cd new_samples/hello_world && go run .`, and Cadence CLI commands as in sample READMEs\n   - BAD: \"Tested locally\" or \"See tests\"\n   - Expect Go/make and/or sample execution commands; no canary or integration server setup required\n\n5. **Potential risks**\n   - Often N/A for sample-only or doc-only changes\n   - Call out when relevant: dependency upgrades, behavior changes for someone copying the sample, build/config changes\n   - Don't require lengthy text when N/A is appropriate\n\n6. **Release notes**\n   - Optional for this repo. Use when change is user-facing (e.g. new sample, notable README change)\n   - Can be N/A for internal refactors or tiny fixes\n   - Don't require lengthy text when N/A is appropriate\n\n7. **Documentation Changes**\n   - Often relevant when adding or changing samples (main README, cmd/samples READMEs, new_samples READMEs including generator-generated, links to cadence or cadence-docs)\n   - Only mark N/A if certain no docs are affected\n\n### Quality Checks\n\n- **Skip obvious things** - Don't flag items clear from folder structure (e.g. area from paths)\n- **Skip trivial refactors** - Minor formatting/style changes don't need deep rationale\n- **Don't check automated items** - CI, linting are automated\n\n## FORBIDDEN - Never Include\n\n- \"Issues Found\", \"Testing Evidence Quality\", \"Documentation Reasoning\", \"Summary\" sections\n- \"Note:\" paragraphs or explanatory text outside recommendations\n- Grouping recommendations by type\n\n## Section Names (Use EXACT Brackets)\n\n- **[Which sample(s) or area?]**\n- **[What changed?]**\n- **[Why?]**\n- **[How did you test it?]**\n- **[Potential risks]**\n- **[Release notes]**\n- **[Documentation Changes]**\n"
  },
  {
    "path": ".github/dco.yml",
    "content": "require:\n  members: false\n"
  },
  {
    "path": ".github/pull_request_guidance.md",
    "content": "<!-- List the area(s) touched so reviewers know where to look.\nThis repo has two sample trees: legacy samples under cmd/samples (built with make, run via ./bin/<name>) and newer samples under new_samples (per-folder, run with go run .). Naming the area helps reviewers.\nExamples: cmd/samples/recipes/helloworld, cmd/samples/batch, cmd/samples/expense, new_samples/hello_world, new_samples/query, new_samples/operations, README, build, config, Makefile -->\n**Which sample(s) or area?**\n\n\n<!-- 1-2 line summary of WHAT changed technically.\n- Link to a GitHub issue when applicable (encouraged for larger changes; optional for trivial doc/sample tweaks)\n- Good: \"Added CancelWorkflow sample in new_samples/operations\" or \"Updated README run instructions for Go 1.21\"\n- Bad: \"updated code\" or \"fixed stuff\" -->\n**What changed?**\n\n\n<!-- Provide context and motivation (see https://cbea.ms/git-commit/#why-not-how). Focus on WHY, not how.\n- Lighter than core Cadence repo: e.g. \"improving clarity for new users,\" \"fixing sample broken on Go 1.21,\" \"aligning with cadence-docs\"\n- Still give enough rationale for reviewers to understand the goal\n- Good: \"HelloWorld didn't show retry behavior; this sample demonstrates RetryOptions so users can copy-paste a working example.\"\n- Bad: \"Improves samples\" -->\n**Why?**\n\n\n<!-- Include concrete, copy-paste commands so another maintainer can reproduce your test steps.\n- Prefer: make, go test ./... (or a targeted test, e.g. go test ./cmd/samples/recipes/helloworld/)\n- For cmd/samples: make then e.g. ./bin/helloworld -m worker and ./bin/helloworld -m trigger\n- For new_samples: cd new_samples/<sample> && go run . and Cadence CLI commands as in the sample README\n- Good: Full commands reviewers can copy-paste to verify\n- Bad: \"Tested locally\" or \"See tests\" -->\n**How did you test it?**\n\n\n<!-- Often N/A for sample-only or doc-only changes. Call out when relevant:\n- Dependency upgrades (e.g. cadence-client version)\n- Behavior changes that could affect someone copying the sample\n- Build or config changes\n- If truly N/A, you can mark it as such -->\n**Potential risks**\n\n\n<!-- Optional for this repo. Use when the change is user-facing (e.g. new sample, notable README change).\n- Can be N/A for internal refactors, tiny fixes, or incremental work -->\n**Release notes**\n\n\n<!-- Did you update the main README, a cmd/samples README, or a new_samples README (including generator-generated READMEs)?\n- Any links to cadence or cadence-docs that need updating?\n- Only mark N/A if you're certain no docs are affected -->\n**Documentation Changes**\n\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "<!-- For guidance on each section, see the PR guidance doc:\n     https://github.com/cadence-workflow/cadence-samples/blob/master/.github/pull_request_guidance.md -->\n**Which sample(s) or area?**\n\n\n**What changed?**\n\n\n**Why?**\n\n\n**How did you test it?**\n\n\n**Potential risks**\n\n\n**Release notes**\n\n\n**Documentation Changes**\n\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build and test cadence-samples\non: [push, pull_request]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v4\n      - name: Setup Go\n        uses: actions/setup-go@v4\n        with:\n          go-version: '1.22.x'\n      - name: Build and Test\n        run: make\n\n"
  },
  {
    "path": ".github/workflows/semantic-pr.yml",
    "content": "name: Semantic Pull Request\n\non:\n  pull_request:\n    types:\n      - opened\n      - edited\n      - synchronize\n\njobs:\n  semantic-pr:\n    name: Validate PR title follows conventional commit format\n    runs-on: ubuntu-latest\n    # TODO: Remove this once we commit to conventional commits\n    continue-on-error: true\n\n    steps:\n      - name: Validate PR title\n        id: lint_pr_title\n        uses: amannn/action-semantic-pull-request@v5.4.0\n        with:\n          # Allow standard conventional commit types\n          types: |\n            fix\n            feat\n            docs\n            style\n            refactor\n            perf\n            test\n            chore\n            ci\n            build\n          # TODO: Remove this once we've decided on scopes\n          requireScope: false\n          # Skip validation for certain labels if needed\n          ignoreLabels: |\n            skip-commit-format\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Comment on PR if validation fails\n        if: steps.lint_pr_title.outputs.error_message != null\n        uses: actions/github-script@v7\n        with:\n          script: |\n            github.rest.issues.createComment({\n              issue_number: context.issue.number,\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              body: `⚠️ **Semantic PR Check Failed**\n\n              **Error Details:**\n              \\`\\`\\`\n              ${{ steps.lint_pr_title.outputs.error_message }}\n              \\`\\`\\`\n\n              **Required Format:**\n              \\`\\`\\`\n              <type>: <description>\n              \\`\\`\\`\n\n              **Allowed types:** fix, feat, docs, style, refactor, perf, test, chore, ci, build\n\n              **Examples:**\n              - \\`feat: add user authentication system\\`\n              - \\`fix: resolve memory leak in worker pool\\`\n              - \\`docs: update API documentation\\`\n              - \\`test: add integration tests for auth flow\\`\n\n              This is currently a **warning only** and won't block your PR from being merged.`\n            })\n"
  },
  {
    "path": ".gitignore",
    "content": "*.out\n*.test\n*.xml\n*.swp\n.idea/\n.vscode/\n*.iml\n*.cov\n*.html\n.tmp/\n.DS_Store\ntest\ntest.log\nvendor/\n# Executables produced by cadence-samples repo\nbin/\n# Binary from go build in new_samples/query\nnew_samples/query/query\ndocker-compose.yml\n\n# Credentials\nnew_samples/client_samples/helloworld_tls/credentials/\n\n# Python SDK Samples\n__pycache__/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Cadence Samples\n\nThis doc is intended for contributors to Cadence samples. Thanks for considering to contribute ❤️\n\n> 📚 **New to contributing to Cadence?** Check out our [Contributing Guide](https://cadenceworkflow.io/community/how-to-contribute/getting-started) for an overview of the contribution process across all Cadence repositories. This document contains cadence-samples specific setup and development instructions.\n\nOnce you go through the rest of this doc and get familiar with local development setup, take a look at the list of issues labeled with\n[good first issue](https://github.com/uber-common/cadence-samples/labels/good%20first%20issue).\n\nJoin our community on the CNCF Slack workspace at [cloud-native.slack.com](https://communityinviter.com/apps/cloud-native/cncf) in the **#cadence-users** channel to reach out and discuss issues with the team.\n\n### Documentation\n\n- Every sample must have a README.md\n- Include:\n  - What the sample demonstrates\n  - Real-world use cases\n  - How to run the sample\n  - Expected output\n  - Key concepts\n\n### Getting Help\n\nIf you need help or have questions:\n\n- Join [CNCF Slack #cadence-users](https://communityinviter.com/apps/cloud-native/cncf)\n- Ask on [StackOverflow](https://stackoverflow.com/questions/tagged/cadence-workflow) with tag `cadence-workflow`\n- Open a [GitHub Discussion](https://github.com/uber-common/cadence-samples/discussions)\n- File an [issue](https://github.com/uber-common/cadence-samples/issues) for bugs\n\n## Code of Conduct\n\nPlease be respectful and constructive in all interactions. We're all here to learn and help each other build better software.\n\n## License\n\nBy contributing, you agree that your contributions will be licensed under the Apache 2.0 License.\n\n---\n\nThank you for contributing to Cadence samples! Your efforts help the entire community. 🚀\n\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction,\nand distribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by\nthe copyright owner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all\nother entities that control, are controlled by, or are under common\ncontrol with that entity. For the purposes of this definition,\n\"control\" means (i) the power, direct or indirect, to cause the\ndirection or management of such entity, whether by contract or\notherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity\nexercising permissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications,\nincluding but not limited to software source code, documentation\nsource, and configuration files.\n\n\"Object\" form shall mean any form resulting from mechanical\ntransformation or translation of a Source form, including but\nnot limited to compiled object code, generated documentation,\nand conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or\nObject form, made available under the License, as indicated by a\ncopyright notice that is included in or attached to the work\n(an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object\nform, that is based on (or derived from) the Work and for which the\neditorial revisions, annotations, elaborations, or other modifications\nrepresent, as a whole, an original work of authorship. For the purposes\nof this License, Derivative Works shall not include works that remain\nseparable from, or merely link (or bind by name) to the interfaces of,\nthe Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including\nthe original version of the Work and any modifications or additions\nto that Work or Derivative Works thereof, that is intentionally\nsubmitted to Licensor for inclusion in the Work by the copyright owner\nor by an individual or Legal Entity authorized to submit on behalf of\nthe copyright owner. For the purposes of this definition, \"submitted\"\nmeans any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems,\nand issue tracking systems that are managed by, or on behalf of, the\nLicensor for the purpose of discussing and improving the Work, but\nexcluding communication that is conspicuously marked or otherwise\ndesignated in writing by the copyright owner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity\non behalf of whom a Contribution has been received by Licensor and\nsubsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\nthis License, each Contributor hereby grants to You a perpetual,\nworldwide, non-exclusive, no-charge, royalty-free, irrevocable\ncopyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the\nWork and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\nthis License, each Contributor hereby grants to You a perpetual,\nworldwide, non-exclusive, no-charge, royalty-free, irrevocable\n(except as stated in this section) patent license to make, have made,\nuse, offer to sell, sell, import, and otherwise transfer the Work,\nwhere such license applies only to those patent claims licensable\nby such Contributor that are necessarily infringed by their\nContribution(s) alone or by combination of their Contribution(s)\nwith the Work to which such Contribution(s) was submitted. If You\ninstitute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work\nor a Contribution incorporated within the Work constitutes direct\nor contributory patent infringement, then any patent licenses\ngranted to You under this License for that Work shall terminate\nas of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\nWork or Derivative Works thereof in any medium, with or without\nmodifications, and in Source or Object form, provided that You\nmeet the following conditions:\n\n(a) You must give any other recipients of the Work or\nDerivative Works a copy of this License; and\n\n(b) You must cause any modified files to carry prominent notices\nstating that You changed the files; and\n\n(c) You must retain, in the Source form of any Derivative Works\nthat You distribute, all copyright, patent, trademark, and\nattribution notices from the Source form of the Work,\nexcluding those notices that do not pertain to any part of\nthe Derivative Works; and\n\n(d) If the Work includes a \"NOTICE\" text file as part of its\ndistribution, then any Derivative Works that You distribute must\ninclude a readable copy of the attribution notices contained\nwithin such NOTICE file, excluding those notices that do not\npertain to any part of the Derivative Works, in at least one\nof the following places: within a NOTICE text file distributed\nas part of the Derivative Works; within the Source form or\ndocumentation, if provided along with the Derivative Works; or,\nwithin a display generated by the Derivative Works, if and\nwherever such third-party notices normally appear. The contents\nof the NOTICE file are for informational purposes only and\ndo not modify the License. You may add Your own attribution\nnotices within Derivative Works that You distribute, alongside\nor as an addendum to the NOTICE text from the Work, provided\nthat such additional attribution notices cannot be construed\nas modifying the License.\n\nYou may add Your own copyright statement to Your modifications and\nmay provide additional or different license terms and conditions\nfor use, reproduction, or distribution of Your modifications, or\nfor any such Derivative Works as a whole, provided Your use,\nreproduction, and distribution of the Work otherwise complies with\nthe conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\nany Contribution intentionally submitted for inclusion in the Work\nby You to the Licensor shall be under the terms and conditions of\nthis License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify\nthe terms of any separate license agreement you may have executed\nwith Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\nnames, trademarks, service marks, or product names of the Licensor,\nexcept as required for reasonable and customary use in describing the\norigin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\nagreed to in writing, Licensor provides the Work (and each\nContributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\nimplied, including, without limitation, any warranties or conditions\nof TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\nPARTICULAR PURPOSE. You are solely responsible for determining the\nappropriateness of using or redistributing the Work and assume any\nrisks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\nwhether in tort (including negligence), contract, or otherwise,\nunless required by applicable law (such as deliberate and grossly\nnegligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special,\nincidental, or consequential damages of any character arising as a\nresult of this License or out of the use or inability to use the\nWork (including but not limited to damages for loss of goodwill,\nwork stoppage, computer failure or malfunction, or any and all\nother commercial damages or losses), even if such Contributor\nhas been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\nthe Work or Derivative Works thereof, You may choose to offer,\nand charge a fee for, acceptance of support, warranty, indemnity,\nor other liability obligations and/or rights consistent with this\nLicense. However, in accepting such obligations, You may act only\non Your own behalf and on Your sole responsibility, not on behalf\nof any other Contributor, and only if You agree to indemnify,\ndefend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason\nof your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\nTo apply the Apache License to your work, attach the following\nboilerplate notice, with the fields enclosed by brackets \"[]\"\nreplaced with your own identifying information. (Don't include\nthe brackets!)  The text should be enclosed in the appropriate\ncomment syntax for the file format. We also recommend that a\nfile or class name and description of purpose be included on the\nsame \"printed page\" as the copyright notice for easier\nidentification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License."
  },
  {
    "path": "Makefile",
    "content": ".PHONY: test bins clean run-generators\nPROJECT_ROOT = github.com/uber-common/cadence-samples\n\nexport PATH := $(GOPATH)/bin:$(PATH)\n\n# default target\ndefault: test\n\nPROGS = helloworld \\\n\tversioning \\\n\tdelaystart \\\n\tbranch \\\n\tchildworkflow \\\n\tcrossdomain \\\n\tchoice \\\n\tdynamic \\\n\tgreetings \\\n\tpickfirst \\\n\tretryactivity \\\n\tsplitmerge \\\n\ttimer \\\n\tlocalactivity \\\n\tquery \\\n\tconsistentquery \\\n\tcron \\\n\ttracing \\\n\tdsl \\\n\tfileprocessing \\\n\texpense_dummy \\\n\texpense \\\n\trecovery \\\n\tcancelactivity \\\n\tctxpropagation \\\n\tpso \\\n\tsignalcounter \\\n\tsideeffect \\\n\tsleep \\\n\tautoscaling-monitoring \\\n\nTEST_ARG ?= -race -v -timeout 5m\nBUILD := ./build\nSAMPLES_DIR=./cmd/samples\n\nexport PATH := $(GOPATH)/bin:$(PATH)\n\n# Automatically gather all srcs\nALL_SRC := $(shell find ./cmd/samples/common -name \"*.go\")\n\n# all directories with *_test.go files in them\nTEST_DIRS=./cmd/samples/cron \\\n\t./cmd/samples/dsl \\\n\t./cmd/samples/expense \\\n\t./cmd/samples/fileprocessing \\\n\t./cmd/samples/recipes/branch \\\n\t./cmd/samples/recipes/choice \\\n\t./cmd/samples/recipes/greetings \\\n\t./cmd/samples/recipes/helloworld \\\n\t./cmd/samples/recipes/delaystart \\\n\t./cmd/samples/recipes/cancelactivity \\\n\t./cmd/samples/recipes/pickfirst \\\n\t./cmd/samples/recipes/mutex \\\n\t./cmd/samples/recipes/retryactivity \\\n\t./cmd/samples/recipes/splitmerge \\\n\t./cmd/samples/recipes/timer \\\n\t./cmd/samples/recipes/localactivity \\\n\t./cmd/samples/recipes/query \\\n\t./cmd/samples/recipes/consistentquery \\\n\t./cmd/samples/recipes/ctxpropagation \\\n\t./cmd/samples/recipes/searchattributes \\\n\t./cmd/samples/recipes/sideeffect \\\n\t./cmd/samples/recipes/signalcounter \\\n\t./cmd/samples/recipes/sleep \\\n\t./cmd/samples/recovery \\\n\t./cmd/samples/pso \\\n\ncancelactivity:\n\tgo build -o bin/cancelactivity cmd/samples/recipes/cancelactivity/*.go\n\nhelloworld:\n\tgo build -o bin/helloworld cmd/samples/recipes/helloworld/*.go\n\ndelaystart:\n\tgo build -o bin/delaystart cmd/samples/recipes/delaystart/*.go\n\nsleep:\n\tgo build -o bin/sleep cmd/samples/recipes/sleep/*.go\n\nbranch:\n\tgo build -o bin/branch cmd/samples/recipes/branch/*.go\n\nchildworkflow:\n\tgo build -o bin/childworkflow cmd/samples/recipes/childworkflow/*.go\n\nchoice:\n\tgo build -o bin/choice cmd/samples/recipes/choice/*.go\n\ndynamic:\n\tgo build -o bin/dynamic cmd/samples/recipes/dynamic/*.go\n\ngreetings:\n\tgo build -o bin/greetings cmd/samples/recipes/greetings/*.go\n\npickfirst:\n\tgo build -o bin/pickfirst cmd/samples/recipes/pickfirst/*.go\n\nmutex:\n\tgo build -o bin/mutex cmd/samples/recipes/mutex/*.go\n\nretryactivity:\n\tgo build -o bin/retryactivity cmd/samples/recipes/retryactivity/*.go\n\nsplitmerge:\n\tgo build -o bin/splitmerge cmd/samples/recipes/splitmerge/*.go\n\nsearchattributes:\n\tgo build -o bin/searchattributes cmd/samples/recipes/searchattributes/*.go\n\ntimer:\n\tgo build -o bin/timer cmd/samples/recipes/timer/*.go\n\nlocalactivity:\n\tgo build -o bin/localactivity cmd/samples/recipes/localactivity/*.go\n\nquery:\n\tgo build -o bin/query cmd/samples/recipes/query/*.go\n\nconsistentquery:\n\tgo build -o bin/consistentquery cmd/samples/recipes/consistentquery/*.go\n\nctxpropagation:\n\tgo build -o bin/ctxpropagation cmd/samples/recipes/ctxpropagation/*.go\n\ntracing:\n\tgo build -o bin/tracing cmd/samples/recipes/tracing/*.go\n\ncron:\n\tgo build -o bin/cron cmd/samples/cron/*.go\n\ndsl:\n\tgo build -o bin/dsl cmd/samples/dsl/*.go\n\nfileprocessing:\n\tgo build -o bin/fileprocessing cmd/samples/fileprocessing/*.go\n\nexpense_dummy:\n\tgo build -o bin/expense_dummy cmd/samples/expense/server/*.go\n\nexpense:\n\tgo build -o bin/expense cmd/samples/expense/*.go\n\nrecovery:\n\tgo build -o bin/recovery cmd/samples/recovery/*.go\n\npso:\n\tgo build -o bin/pso cmd/samples/pso/*.go\n\n\nsignalcounter:\n\tgo build -o bin/signalcounter cmd/samples/recipes/signalcounter/*.go\n\ncrossdomain:\n\tgo build -o bin/crossdomain cmd/samples/recipes/crossdomain/*.go\n\ncrossdomain-setup:\n\t# use the ..cadence-server --env development_xdc_cluster0 ... to set up three\n\tcadence --ad 127.0.0.1:7933 --env development --do domain0 domain register --ac cluster0 --gd true --clusters cluster0 cluster1 # global domain required\n\tcadence --ad 127.0.0.1:7933 --env development --do domain1 domain register --ac cluster1 --gd true --clusters cluster0 cluster1\n\tcadence --ad 127.0.0.1:7933 --env development --do domain2 domain register --ac cluster0 --gd true --clusters cluster0 cluster1\n\ncrossdomain-run: crossdomain\n\ttmux split-window -h './bin/crossdomain -m \"worker0\"' \\; \\\n\t\tsplit-window -v './bin/crossdomain -m \"worker1\"' \\; \\\n\t\tsplit-window -v './bin/crossdomain -m \"worker2\"'\n\nsideeffect:\n\tgo build -o bin/sideeffect cmd/samples/recipes/sideeffect/*.go\n\nversioning:\n\tgo build -o bin/versioning cmd/samples/recipes/versioning/*.go\n\nautoscaling-monitoring:\n\tgo build -o bin/autoscaling-monitoring cmd/samples/advanced/autoscaling-monitoring/*.go\n\nrun-generators:\n\t@echo \"Running generators in new_samples...\"\n\t@for dir in new_samples/*/generator; do \\\n\t\tif [ -d \"$$dir\" ]; then \\\n\t\t\techo \"Running generator in $$dir\"; \\\n\t\t\t(cd $$dir && go run .); \\\n\t\tfi; \\\n\tdone\n\t@echo \"All generators completed\"\n\ntest: bins\n\t@rm -f test\n\t@rm -f test.log\n\t@echo $(TEST_DIRS)\n\t@for dir in $(TEST_DIRS); do \\\n\t\tgo test -coverprofile=$@ \"$$dir\" | tee -a test.log; \\\n\tdone;\n\nclean:\n\trm -rf bin\n\trm -Rf $(BUILD)\n\nbins: helloworld \\\n\tversioning \\\n\tdelaystart \\\n\tbranch \\\n\tcrossdomain \\\n\tchildworkflow \\\n\tchoice \\\n\tdynamic \\\n\tgreetings \\\n\tpickfirst \\\n\tmutex \\\n\tcancelactivity \\\n\tretryactivity \\\n\tsplitmerge \\\n\tsearchattributes \\\n\ttimer \\\n\tcron \\\n\ttracing \\\n\tdsl \\\n\tfileprocessing \\\n\texpense_dummy \\\n\texpense \\\n\tlocalactivity \\\n\tquery \\\n\tconsistentquery \\\n\trecovery \\\n\tctxpropagation \\\n\tpso \\\n\tsignalcounter \\\n\tsideeffect \\\n\tsleep \\\n\tautoscaling-monitoring \\\n"
  },
  {
    "path": "NOTICE",
    "content": "Copyright (c) 2025 Uber Technologies, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in\n all copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Cadence Samples ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/uber-common/cadence-samples/build.yml)\n\nWelcome to the Cadence Samples repository! This collection demonstrates the powerful capabilities of Cadence workflow orchestration through practical, real-world examples. Whether you're new to Cadence or looking to implement specific patterns, these samples will help you understand how to build reliable, scalable, and maintainable workflow applications.\n\n## What is Cadence?\n\nCadence is a distributed, scalable, durable, and highly available orchestration engine that helps developers build reliable applications. It provides:\n\n- **Reliability**: Automatic retry mechanisms, error handling, and fault tolerance\n- **Scalability**: Distributed execution across multiple workers\n- **Durability**: Persistent workflow state that survives failures\n- **Observability**: Built-in monitoring, tracing, and querying capabilities\n\nLearn more about Cadence at:\n- [Documentation](https://cadenceworkflow.io)\n- [Cadence Server](https://github.com/cadence-workflow/cadence)\n- [Cadence Go Client](https://github.com/cadence-workflow/cadence-go-client)\n- [CNCF Slack](https://communityinviter.com/apps/cloud-native/cncf) - Join **#cadence-users** channel on CNCF Slack\n\n## 🚀 Quick Start\n\n### Prerequisites\n\n1. **Clone the Repository**:\n```bash\ngit clone https://github.com/uber-common/cadence-samples.git && cd cadence-samples\n```\n\n2. **Start Cadence Server**\n```bash\ncurl -LO https://raw.githubusercontent.com/cadence-workflow/cadence/refs/heads/master/docker/docker-compose.yml && docker-compose up --wait\n```\n\nThis downloads and starts all required dependencies including Cadence server, database, and [Cadence Web UI](https://github.com/uber/cadence-web). You can view your sample workflows at [http://localhost:8088](http://localhost:8088).\n\n3. **Build All Samples**:\n```bash\nmake\n```\n\n### Docker Troubleshooting\n\nThe `docker-compose` command requires Docker daemon to be running. On macOS/Windows, open Docker Desktop. On Linux, run `sudo systemctl start docker`.\n\n<details>\n<summary>Port conflicts</summary>\n\nIf you see `Bind for 0.0.0.0:<port> failed: port is already allocated`, find the process using that port:\n\n```bash\nlsof -i tcp:<port>\n```\n\nTo find which container is using it:\n```bash\ndocker ps --format '{{.ID}}\\t{{.Ports}}\\t{{.Names}}' | grep <port>\n```\n\nStop and remove the conflicting container:\n```bash\ndocker stop <container_id> && docker rm <container_id>\n```\n\nCadence uses these ports: `7833`, `7933`, `7939`, `8000-8003`, `8088` (Web UI), `9042` (Cassandra), `9090` (Prometheus), `3000` (Grafana).\n\n</details>\n\n<details>\n<summary>Reset everything</summary>\n\n```bash\ndocker-compose down\ndocker ps -a  # check for leftover containers\ndocker rm <container_id>  # remove if needed\n```\n\nVerify with `docker ps` and visit [http://localhost:8088](http://localhost:8088).\n\n</details>\n\n## 📚 Sample Categories\n\n### Table of Contents\n- [🎯 Basic Examples](#-basic-examples)\n  - [Hello World](#hello-world)\n  - [Greetings](#greetings)\n  - [Cron](#cron)\n  - [Timer](#timer)\n  - [Delay Start](#delay-start)\n  - [Branch](#branch)\n  - [Split-Merge](#split-merge)\n  - [Pick First](#pick-first)\n\n- [🔧 Advanced Examples](#-advanced-examples)\n  - [Choice](#choice)\n  - [Retry Activity](#retry-activity)\n  - [Cancel Activity](#cancel-activity)\n  - [Mutex](#mutex)\n  - [Query](#query)\n  - [Consistent Query](#consistent-query)\n  - [Child Workflow](#child-workflow)\n  - [Dynamic](#dynamic)\n  - [Local Activity](#local-activity)\n  - [Versioning](#versioning)\n  - [Search Attributes](#search-attributes)\n  - [Context Propagation](#context-propagation)\n  - [Tracing](#tracing)\n  - [Side Effect](#side-effect)\n  - [Recovery](#recovery)\n\n- [🏢 Business Application Examples](#-business-application-examples)\n  - [Expense](#expense)\n  - [File Processing](#file-processing)\n  - [DSL](#dsl)\n  - [PSO (Particle Swarm Optimization)](#pso-particle-swarm-optimization)\n\n---\n\n### 🎯 **Basic Examples**\n\n#### Hello World\n* **Shows**: Basic Cadence workflow concepts and activity execution.\n* **What it does**: Executes a single activity that returns a greeting message.\n* **Real-world use case**: Foundation for understanding workflow structure, activity execution, and basic error handling.\n* **Key concepts**: Workflow definition, activity execution, error handling, worker setup.\n* **Source code**: [cmd/samples/recipes/helloworld/](cmd/samples/recipes/helloworld/)\n\n##### How to run\nStart Worker:\n```bash\n./bin/helloworld -m worker\n```\n\nStart Workflow:\n```bash\n./bin/helloworld -m trigger\n```\n\n#### Greetings\n* **Shows**: Sequential activity execution and result passing between activities.\n* **What it does**: Executes three activities in sequence: get greeting, get name, then combine them.\n* **Real-world use case**: Multi-step processes like user registration, order processing, or data transformation pipelines.\n* **Key concepts**: Sequential execution, activity chaining, result passing between activities.\n* **Source code**: [cmd/samples/recipes/greetings/](cmd/samples/recipes/greetings/)\n\n##### How to run\nStart Worker:\n```bash\n./bin/greetings -m worker\n```\n\nStart Workflow:\n```bash\n./bin/greetings -m trigger\n```\n\n#### Cron\n* **Shows**: Automated recurring tasks and cron scheduling.\n* **What it does**: Executes a workflow based on cron expressions (e.g., every minute, daily at 2 AM).\n* **Real-world use case**: Data backups, report generation, system maintenance, periodic data synchronization.\n* **Key concepts**: Cron scheduling, workflow persistence, time-based execution.\n* **Source code**: [cmd/samples/cron/](cmd/samples/cron/)\n\n##### How to run\nStart Worker:\n```bash\n./bin/cron -m worker\n```\n\nStart Workflow:\n```bash\n./bin/cron -m trigger -cron \"* * * * *\"  # Run every minute\n```\n\n#### Timer\n* **Shows**: Timeout and delay handling with parallel execution.\n* **What it does**: Starts a long-running process and sends a notification if it takes too long.\n* **Real-world use case**: Order processing with SLA monitoring, payment processing with timeout alerts, API calls with fallback mechanisms.\n* **Key concepts**: Timer creation, timeout handling, parallel execution with cancellation.\n* **Source code**: [cmd/samples/recipes/timer/](cmd/samples/recipes/timer/)\n\n##### How to run\nStart Worker:\n```bash\n./bin/timer -m worker\n```\n\nStart Workflow:\n```bash\n./bin/timer -m trigger\n```\n\n#### Delay Start\n* **Shows**: Deferred execution and delayed workflow execution.\n* **What it does**: Waits for a specified duration before executing the main workflow logic.\n* **Real-world use case**: Scheduled maintenance windows, delayed notifications, batch processing at specific times.\n* **Key concepts**: Delayed execution, time-based workflow scheduling.\n* **Source code**: [cmd/samples/recipes/delaystart/](cmd/samples/recipes/delaystart/)\n\n##### How to run\nStart Worker:\n```bash\n./bin/delaystart -m worker\n```\n\nStart Workflow:\n```bash\n./bin/delaystart -m trigger\n```\n\n### 🔄 **Parallel Execution Examples**\n\n#### Branch\n* **Shows**: Parallel activity execution and concurrent activity management.\n* **What it does**: Executes multiple activities in parallel and waits for all to complete.\n* **Real-world use case**: Processing multiple orders simultaneously, calling multiple APIs in parallel, batch data processing.\n* **Key concepts**: Parallel execution, Future handling, concurrent activity management.\n* **Source code**: [cmd/samples/recipes/branch/](cmd/samples/recipes/branch/)\n\n##### How to run\nStart Worker:\n```bash\n./bin/branch -m worker\n```\n\nStart Single Branch Workflow:\n```bash\n./bin/branch -m trigger -c branch\n```\n\nStart Parallel Branch Workflow:\n```bash\n./bin/branch -m trigger -c parallel\n```\n\n#### Split-Merge\n* **Shows**: Divide and conquer pattern with parallel processing.\n* **What it does**: Splits a large task into chunks, processes them in parallel, then merges results.\n* **Real-world use case**: Large file processing, batch data analysis, image/video processing, ETL pipelines.\n* **Key concepts**: Work splitting, parallel processing, result aggregation, worker coordination.\n* **Source code**: [cmd/samples/recipes/splitmerge/](cmd/samples/recipes/splitmerge/)\n\n##### How to run\nStart Worker:\n```bash\n./bin/splitmerge -m worker\n```\n\nStart Workflow:\n```bash\n./bin/splitmerge -m trigger\n```\n\n#### Pick First\n* **Shows**: Race condition handling and activity cancellation.\n* **What it does**: Runs multiple activities in parallel and uses the result from whichever completes first.\n* **Real-world use case**: Multi-provider API calls, redundant service calls, failover mechanisms, load balancing.\n* **Key concepts**: Parallel execution, cancellation, race condition handling.\n* **Source code**: [cmd/samples/recipes/pickfirst/](cmd/samples/recipes/pickfirst/)\n\n##### How to run\nStart Worker:\n```bash\n./bin/pickfirst -m worker\n```\n\nStart Workflow:\n```bash\n./bin/pickfirst -m trigger\n```\n\n### 🔧 **Advanced Examples**\n\n#### Choice\n* **Shows**: Conditional execution and decision-based activity routing.\n* **What it does**: Executes different activities based on the result of a decision activity.\n* **Real-world use case**: Order routing based on type, user authentication flows, approval workflows, conditional processing.\n* **Key concepts**: Conditional logic, decision trees, workflow branching.\n* **Source code**: [cmd/samples/recipes/choice/](cmd/samples/recipes/choice/)\n\n##### How to run\nStart Worker:\n```bash\n./bin/choice -m worker\n```\n\nStart Single Choice Workflow:\n```bash\n./bin/choice -m trigger -c single\n```\n\nStart Multi-Choice Workflow:\n```bash\n./bin/choice -m trigger -c multi\n```\n\n#### Retry Activity\n* **Shows**: Resilient processing with retry policies and heartbeat tracking.\n* **What it does**: Demonstrates activity retry policies with heartbeat progress tracking.\n* **Real-world use case**: API calls with intermittent failures, database operations, external service integration.\n* **Key concepts**: Retry policies, heartbeat mechanisms, progress tracking, failure recovery.\n* **Source code**: [cmd/samples/recipes/retryactivity/](cmd/samples/recipes/retryactivity/)\n\n##### How to run\nStart Worker:\n```bash\n./bin/retryactivity -m worker\n```\n\nStart Workflow:\n```bash\n./bin/retryactivity -m trigger\n```\n\n#### Cancel Activity\n* **Shows**: Graceful cancellation and cleanup operations.\n* **What it does**: Shows how to cancel running activities and perform cleanup operations.\n* **Real-world use case**: User-initiated cancellations, timeout handling, resource cleanup, emergency stops.\n* **Key concepts**: Cancellation handling, cleanup operations, graceful shutdown.\n* **Source code**: [cmd/samples/recipes/cancelactivity/](cmd/samples/recipes/cancelactivity/)\n\n##### How to run\nStart Worker:\n```bash\n./bin/cancelactivity -m worker\n```\n\nStart Workflow:\n```bash\n./bin/cancelactivity -m trigger\n```\n\n**Cancel Workflow:**\n```bash\n./bin/cancelactivity -m cancel -w <WorkflowID>\n```\n\n#### Mutex\n* **Shows**: Resource locking and distributed locking patterns.\n* **What it does**: Ensures only one workflow can access a specific resource at a time.\n* **Real-world use case**: Database migrations, configuration updates, resource allocation, critical section protection.\n* **Key concepts**: Distributed locking, resource coordination, mutual exclusion.\n* **Source code**: [cmd/samples/recipes/mutex/](cmd/samples/recipes/mutex/)\n\n##### How to run\n* Check **[Detailed Guide](cmd/samples/recipes/mutex/README.md)** to run the sample\n\n#### Query\n* **Shows**: Workflow state inspection and custom query handlers.\n* **What it does**: Demonstrates custom query handlers to inspect workflow state.\n* **Real-world use case**: Progress monitoring, status dashboards, debugging running workflows, user interfaces.\n* **Key concepts**: Query handlers, state inspection, workflow monitoring.\n* **Source code**: [cmd/samples/recipes/query/](cmd/samples/recipes/query/)\n\n##### How to run\n* Check **[Detailed Guide](cmd/samples/recipes/query/README.md)** to run the sample\n\n#### Consistent Query\n* **Shows**: Consistent state queries and signal handling.\n* **What it does**: Shows how to query workflow state consistently while handling signals.\n* **Real-world use case**: Real-time dashboards, progress tracking, state synchronization.\n* **Key concepts**: Consistent queries, signal handling, state management.\n* **Source code**: [cmd/samples/recipes/consistentquery/](cmd/samples/recipes/consistentquery/)\n\n##### How to run\n* Check **[Detailed Guide](cmd/samples/recipes/consistentquery/README.md)** to run the sample\n\n#### Child Workflow\n* **Shows**: Workflow composition and parent-child workflow relationships.\n* **What it does**: Demonstrates parent-child workflow relationships with ContinueAsNew pattern.\n* **Real-world use case**: Complex business processes, workflow decomposition, modular workflow design.\n* **Key concepts**: Child workflows, ContinueAsNew, workflow composition.\n* **Source code**: [cmd/samples/recipes/childworkflow/](cmd/samples/recipes/childworkflow/)\n\n##### How to run\nStart Worker:\n```bash\n./bin/childworkflow -m worker\n```\n\nStart Workflow:\n```bash\n./bin/childworkflow -m trigger\n```\n\n#### Dynamic\n* **Shows**: Dynamic activity invocation and string-based execution.\n* **What it does**: Demonstrates calling activities using string names for dynamic behavior.\n* **Real-world use case**: Plugin systems, dynamic workflow composition, configuration-driven workflows.\n* **Key concepts**: Dynamic activity invocation, string-based execution, flexible workflow design.\n* **Source code**: [cmd/samples/recipes/dynamic/](cmd/samples/recipes/dynamic/)\n\n##### How to run\nStart Worker:\n```bash\n./bin/dynamic -m worker\n```\n\nStart Workflow:\n```bash\n./bin/dynamic -m trigger\n```\n\n#### Local Activity\n* **Shows**: High-performance local execution and lightweight operations.\n* **What it does**: Shows how to use local activities for quick operations that don't need external execution.\n* **Real-world use case**: Data validation, simple calculations, condition checking, fast decision making.\n* **Key concepts**: Local activities, performance optimization, lightweight operations.\n* **Source code**: [cmd/samples/recipes/localactivity/](cmd/samples/recipes/localactivity/)\n\n##### How to run\n* Check **[Detailed Guide](cmd/samples/recipes/localactivity/README.md)** to run the sample\n\n#### Versioning\n* **Shows**: Safe workflow evolution and backward compatibility.\n* **What it does**: Shows workflow versioning with backward compatibility and safe rollbacks.\n* **Real-world use case**: Production deployments, feature rollouts, backward compatibility, safe migrations.\n* **Key concepts**: Workflow versioning, backward compatibility, safe deployments.\n* **Source code**: [cmd/samples/recipes/versioning/](cmd/samples/recipes/versioning/)\n\n##### How to run\n* Check **[Detailed Guide](cmd/samples/recipes/versioning/README.md)** to run the sample\n\n#### Search Attributes\n* **Shows**: Workflow indexing and search for workflow discovery.\n* **What it does**: Shows how to add searchable attributes to workflows and query them.\n* **Real-world use case**: Workflow discovery, filtering, reporting, operational dashboards.\n* **Key concepts**: Search attributes, workflow indexing, ElasticSearch integration.\n* **Source code**: [cmd/samples/recipes/searchattributes/](cmd/samples/recipes/searchattributes/)\n\n##### How to run\n* Check **[Detailed Guide](cmd/samples/recipes/searchattributes/README.md)** to run the sample\n\n#### Context Propagation\n* **Shows**: Cross-workflow context and context propagation.\n* **What it does**: Demonstrates passing context (like user info, trace IDs) through workflow execution.\n* **Real-world use case**: Distributed tracing, user context propagation, audit trails, debugging.\n* **Key concepts**: Context propagation, distributed tracing, cross-service context.\n* **Source code**: [cmd/samples/recipes/ctxpropagation/](cmd/samples/recipes/ctxpropagation/)\n\n##### How to run\n* Check **[Detailed Guide](cmd/samples/recipes/ctxpropagation/README.md)** to run the sample\n\n#### Tracing\n* **Shows**: Distributed tracing and integration with tracing systems.\n* **What it does**: Shows how to add distributed tracing to Cadence workflows.\n* **Real-world use case**: Performance monitoring, debugging, observability, APM integration.\n* **Key concepts**: Distributed tracing, Jaeger integration, observability.\n* **Source code**: [cmd/samples/recipes/tracing/](cmd/samples/recipes/tracing/)\n\n##### How to run\nStart Worker:\n```bash\n./bin/tracing -m worker\n```\n\nStart Workflow:\n```bash\n./bin/tracing -m trigger\n```\n\n#### Side Effect\n* **Shows**: Non-deterministic operations and replay safety.\n* **What it does**: Demonstrates the SideEffect API for handling non-deterministic operations.\n* **Real-world use case**: ID generation, random number generation, external state queries.\n* **Key concepts**: Side effects, non-deterministic operations, replay safety.\n* **Source code**: [cmd/samples/recipes/sideeffect/](cmd/samples/recipes/sideeffect/)\n\n##### How to run\nStart Workflow:\n```bash\n./bin/sideeffect\n```\n\n#### Recovery\n* **Shows**: Workflow recovery and failure handling.\n* **What it does**: Shows how to restart failed workflows and replay signals.\n* **Real-world use case**: Disaster recovery, workflow repair, system restoration.\n* **Key concepts**: Workflow recovery, signal replay, failure handling.\n* **Source code**: [cmd/samples/recovery/](cmd/samples/recovery/)\n\n##### How to run\n* Check **[Detailed Guide](cmd/samples/recovery/README.md)** to run the sample\n\n### 🏢 **Business Application Examples**\n\n#### Expense\n* **Shows**: Human-in-the-loop workflows and approval workflows.\n* **What it does**: Creates an expense report, waits for approval, then processes payment.\n* **Real-world use case**: Expense approval, purchase orders, document review, approval workflows.\n* **Key concepts**: Human-in-the-loop, async completion, approval workflows.\n* **Source code**: [cmd/samples/expense/](cmd/samples/expense/)\n\n##### How to run\n* Check **[Detailed Guide](cmd/samples/expense/README.md)** to run the sample\n\n#### File Processing\n* **Shows**: Distributed file processing across multiple hosts.\n* **What it does**: Downloads, processes, and uploads files with host-specific execution.\n* **Real-world use case**: Large file processing, ETL pipelines, media processing, data transformation.\n* **Key concepts**: File processing, host-specific execution, session management, retry policies.\n* **Source code**: [cmd/samples/fileprocessing/](cmd/samples/fileprocessing/)\n\n##### How to run\n* Check **[Detailed Guide](cmd/samples/fileprocessing/README.md)** to run the sample\n\n#### DSL\n* **Shows**: Domain-specific language and custom workflow language creation.\n* **What it does**: Implements a simple DSL for defining workflows using YAML configuration.\n* **Real-world use case**: Business user workflow definition, configuration-driven workflows, workflow templates.\n* **Key concepts**: DSL implementation, YAML parsing, dynamic workflow creation.\n* **Source code**: [cmd/samples/dsl/](cmd/samples/dsl/)\n\n##### How to run\n* Check **[Detailed Guide](cmd/samples/dsl/README.md)** to run the sample\n\n#### PSO (Particle Swarm Optimization)\n* **Shows**: Complex mathematical workflows and long-running optimization workflows.\n* **What it does**: Implements particle swarm optimization with child workflows and ContinueAsNew.\n* **Real-world use case**: Mathematical optimization, machine learning training, complex calculations.\n* **Key concepts**: Long-running workflows, ContinueAsNew, child workflows, custom data converters.\n* **Source code**: [cmd/samples/pso/](cmd/samples/pso/)\n\n##### How to run\n* Check **[Detailed Guide](cmd/samples/pso/README.md)** to run the sample\n\n## 🛠 **Development & Testing**\n\n### Building Samples\n```bash\nmake\n```\n\n### Running Tests\n```bash\n# Run all tests\ngo test ./...\n\n# Run specific sample tests\ngo test ./cmd/samples/recipes/helloworld/\n```\n\n### Worker Modes\nMost samples support these modes:\n- `worker`: Start a worker to handle workflow execution\n- `trigger`: Start a new workflow execution\n- `query`: Query a running workflow (where applicable)\n- `signal`: Send a signal to a workflow (where applicable)\n\n\n## 🤝 **Contributing**\n\nWe welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.\n\n## 📄 **License**\n\nApache 2.0 License - see [LICENSE](LICENSE) for details.\n\n## 🆘 **Getting Help**\n\n- **Documentation**: [Cadence Documentation](https://cadenceworkflow.io/docs/)\n- **Community**: [Cadence Community](https://cadenceworkflow.io/community/)\n- **Issues**: [GitHub Issues](https://github.com/uber-common/cadence-samples/issues)\n\n---\n\n**Happy Workflowing! 🚀**"
  },
  {
    "path": "cmd/samples/advanced/autoscaling-monitoring/Makefile",
    "content": "# Makefile for autoscaling monitoring sample\n\n.PHONY: build clean test\n\n# Build the autoscaling monitoring sample\nbuild:\n\tgo build -o ../../../../bin/autoscaling-monitoring *.go\n\n# Clean build artifacts\nclean:\n\trm -f ../../../../bin/autoscaling-monitoring\n\n# Run tests\ntest:\n\tgo test -v .\n\n# Install dependencies\ndeps:\n\tgo mod tidy\n\n# Run the sample in different modes\nrun-worker: build\n\t../../../../bin/autoscaling-monitoring -m worker\n\nrun-trigger: build\n\t../../../../bin/autoscaling-monitoring -m trigger\n"
  },
  {
    "path": "cmd/samples/advanced/autoscaling-monitoring/README.md",
    "content": "# Autoscaling Monitoring Sample\n\nThis sample demonstrates three advanced Cadence worker features:\n\n1. **Worker Poller Autoscaling** - Dynamic adjustment of worker poller goroutines based on workload\n2. **Integrated Prometheus Metrics** - Real-time metrics collection using Tally with Prometheus reporter\n3. **Autoscaling Metrics** - Comprehensive autoscaling behavior metrics exposed via HTTP endpoint\n\n## Features\n\n### Worker Poller Autoscaling\nThe worker uses `worker.NewV2` with `AutoScalerOptions` to enable true autoscaling behavior:\n- **AutoScalerOptions.Enabled**: true - Enables the autoscaling feature\n- **PollerMinCount**: 2 - Minimum number of poller goroutines\n- **PollerMaxCount**: 8 - Maximum number of poller goroutines  \n- **PollerInitCount**: 4 - Initial number of poller goroutines\n\nThe worker automatically adjusts the number of poller goroutines between the min and max values based on the current workload.\n\n### Prometheus Metrics\nThe sample uses Tally with Prometheus reporter to expose comprehensive metrics:\n- **Real-time autoscaling metrics** - Poller count changes, quota adjustments, wait times\n- **Worker performance metrics** - Task processing rates, poller utilization, queue depths\n- **Standard Cadence metrics** - All metrics automatically emitted by the Cadence Go client\n- **Sanitized metric names** - Prometheus-compatible metric names and labels\n\n### Monitoring Dashboards\nWhen running the Cadence server locally with Grafana, you can access the client dashboards at:\n\n**Client Dashboards**: http://localhost:3000/d/dehkspwgabvuoc/cadence-client\n\n> **Note**: Make sure to select a Domain in Grafana for the dashboards to display data. The dashboards will be empty until a domain is selected from the dropdown.\n\n\n## Prerequisites\n\n1. **Cadence Server**: Running locally with Docker Compose.\n2. **Prometheus**: Configured to scrape metrics from the sample.\n3. **Grafana**: With Cadence dashboards (included with default Cadence server setup). Dashboards in the latest version of the server.\n\n## Quick Start\n\n### 1. Start the Worker\n```bash\n./bin/autoscaling-monitoring -m worker\n```\n\nThe worker automatically exposes metrics at: http://127.0.0.1:8004/metrics\n\n### 2. Generate Load\n```bash\n./bin/autoscaling-monitoring -m trigger\n```\n\n## Configuration\n\nThe sample uses a custom configuration system that extends the base Cadence configuration. You can specify a configuration file using the `-config` flag:\n\n```bash\n./bin/autoscaling-monitoring -m worker -config /path/to/config.yaml\n```\n\n### Configuration File Structure\n\n```yaml\n# Cadence connection settings\ndomain: \"default\"\nservice: \"cadence-frontend\"\nhost: \"localhost:7833\"\n\n# Prometheus configuration\nprometheus:\n  listenAddress: \"127.0.0.1:8004\"\n\n# Autoscaling configuration\nautoscaling:\n  # Worker autoscaling settings\n  pollerMinCount: 2\n  pollerMaxCount: 8\n  pollerInitCount: 4\n  \n  # Load generation settings\n  loadGeneration:\n    # Workflow-level settings\n    workflows: 10             # Number of workflows to start\n    workflowDelay: 1000       # Delay between starting workflows (milliseconds)\n    \n    # Activity-level settings (per workflow)\n    activitiesPerWorkflow: 30 # Number of activities per workflow\n    batchDelay: 2000          # Delay between activity batches within workflow (milliseconds)\n    \n    # Activity processing time range (milliseconds)\n    minProcessingTime: 1000\n    maxProcessingTime: 6000\n```\n\n### Configuration Usage\n\nThe configuration values are used throughout the sample:\n\n1. **Worker Configuration** (`worker_config.go`):\n   - `pollerMinCount`, `pollerMaxCount`, `pollerInitCount` → `AutoScalerOptions`\n\n2. **Workflow Configuration** (`workflow.go`):\n   - `activitiesPerWorkflow` → Number of activities to execute per workflow\n   - `batchDelay` → Delay between activity batches within workflow\n\n3. **Activity Configuration** (`activities.go`):\n   - `minProcessingTime`, `maxProcessingTime` → Activity processing time range\n\n4. **Prometheus Configuration** (integrated):\n   - `listenAddress` → Metrics endpoint port (default: 127.0.0.1:8004)\n\n### Default Configuration\n\nIf no configuration file is provided or if the file cannot be read, the sample uses these defaults:\n\n```yaml\ndomain: \"default\"\nservice: \"cadence-frontend\"\nhost: \"localhost:7833\"\nprometheus:\n  listenAddress: \"127.0.0.1:8004\"\nautoscaling:\n  pollerMinCount: 2\n  pollerMaxCount: 8\n  pollerInitCount: 4\n  loadGeneration:\n    workflows: 10\n    workflowDelay: 1000\n    activitiesPerWorkflow: 30\n    batchDelay: 2000\n    minProcessingTime: 1000\n    maxProcessingTime: 6000\n```\n\n### Load Pattern Examples\n\nThe sample supports various load patterns for testing autoscaling behavior:\n\n#### **1. Gradual Ramp-up (Default)**\n```yaml\nloadGeneration:\n  workflows: 10\n  workflowDelay: 1000\n  activitiesPerWorkflow: 30\n```\n**Result**: 10 workflows starting 1 second apart, each with 30 activities (300 total activities)\n\n#### **2. Burst Load**\n```yaml\nloadGeneration:\n  workflows: 25\n  workflowDelay: 0\n  activitiesPerWorkflow: 60\n```\n**Result**: 25 workflows all starting immediately (1500 total activities)\n\n#### **3. Sustained Load**\n```yaml\nloadGeneration:\n  workflows: 50\n  workflowDelay: 2000\n  activitiesPerWorkflow: 100\n```\n**Result**: 5 long-running workflows with 2-second delays between starts (5000 total activities)\n\n#### **4. Light Load**\n```yaml\nloadGeneration:\n  workflows: 1\n  workflowDelay: 0\n  activitiesPerWorkflow: 20\n```\n**Result**: Single workflow with 20 activities for minimal load testing\n\n## Monitoring\n\n### Metrics Endpoints\n- **Prometheus Metrics**: http://127.0.0.1:8004/metrics\n  - Exposed automatically when running worker mode only\n  - Real-time autoscaling and worker performance metrics\n  - Prometheus-compatible format with sanitized names\n  - **Note**: Metrics server is not started in trigger mode\n\n### Grafana Dashboard\nAccess the Cadence client dashboard at: http://localhost:3000/d/dehkspwgabvuoc/cadence-client\n\n### Key Metrics to Monitor\n\n1. **Worker Performance Metrics**:\n   - `cadence_worker_decision_poll_success_count` - Successful decision task polls\n   - `cadence_worker_activity_poll_success_count` - Successful activity task polls\n   - `cadence_worker_decision_poll_count` - Total decision task poll attempts\n   - `cadence_worker_activity_poll_count` - Total activity task poll attempts\n\n2. **Autoscaling Behavior Metrics**:\n   - `cadence_worker_poller_count` - Number of active poller goroutines (key autoscaling indicator)\n   - `cadence_concurrency_auto_scaler_poller_quota` - Current poller quota for autoscaling\n   - `cadence_concurrency_auto_scaler_poller_wait_time` - Time pollers wait for tasks\n   - `cadence_concurrency_auto_scaler_scale_up_count` - Number of scale-up events\n   - `cadence_concurrency_auto_scaler_scale_down_count` - Number of scale-down events\n\n## How It Works\n\n### Load Generation\nThe sample creates multiple workflows that execute activities in parallel, with each workflow:\n- Starting with configurable delays (`workflowDelay`) to create sustained load patterns\n- Executing a configurable number of activities (`activitiesPerWorkflow`) per workflow\n- Each activity taking 1-6 seconds to complete (configurable via `minProcessingTime`/`maxProcessingTime`)\n- Recording metrics about execution time\n- Creating varying load patterns with configurable batch delays within each workflow\n\n### Autoscaling Demonstration\nThe worker uses `worker.NewV2` with `AutoScalerOptions` to:\n- Start with configurable poller goroutines (`pollerInitCount`)\n- Scale down to minimum pollers (`pollerMinCount`) when load is low\n- Scale up to maximum pollers (`pollerMaxCount`) when load is high\n- Automatically adjust based on task queue depth and processing time\n\n### Metrics Collection\nThe sample uses Tally with Prometheus reporter for comprehensive metrics:\n- **Real-time autoscaling metrics** - Poller count changes, quota adjustments, scale events\n- **Worker performance metrics** - Task processing rates, poller utilization, queue depths\n- **Standard Cadence metrics** - All metrics automatically emitted by the Cadence Go client\n- **Sanitized metric names** - Prometheus-compatible format with proper character replacement\n\n## Production Considerations\n\n### Scaling\n- Adjust `pollerMinCount`, `pollerMaxCount`, and `pollerInitCount` based on your workload\n- Monitor worker performance and adjust autoscaling parameters\n- Use multiple worker instances for high availability\n\n### Monitoring\n- Configure Prometheus to scrape metrics regularly (latest version of Cadence server is configured to do this)\n- Set up alerts for worker performance issues\n- Use Grafana dashboards to visualize autoscaling behavior\n- Monitor poller count changes to verify autoscaling is working\n\n### Security\n- Secure the Prometheus endpoint in production\n- Use authentication for metrics access\n- Consider using HTTPS for metrics endpoints\n\n## Testing\n\nThe sample includes unit tests for the configuration loading functionality. Run these tests if you make any changes to the config:\n\n### Running Tests\n```bash\n# Run all tests\ngo test -v\n\n# Run specific test\ngo test -v -run TestLoadConfiguration_SuccessfulLoading\n\n# Run tests with coverage\ngo test -v -cover\n```\n\n### Test Coverage\nThe tests cover:\n- **Successful configuration loading** - Complete YAML files with all fields\n- **Missing file fallback** - Graceful handling when config file doesn't exist\n- **Default value application** - Ensuring all fields have sensible defaults\n\n### Configuration Testing\nThe tests validate that the improved configuration system:\n- Handles embedded struct issues properly\n- Applies defaults correctly for missing fields\n- Provides clear error messages for configuration problems\n- Maintains backward compatibility\n\n## Troubleshooting\n\n### Common Issues\n\n1. **Worker Not Starting**:\n   - Check Cadence server is running\n   - Verify domain exists\n   - Check configuration file\n   - Ensure using compatible Cadence client version\n\n2. **Autoscaling Not Working**:\n   - Verify `worker.NewV2` is being used\n   - Check `AutoScalerOptions.Enabled` is true\n   - Monitor poller count changes in logs\n   - Ensure sufficient load is being generated\n\n3. **Configuration Issues**:\n   - Verify configuration file path is correct\n   - Check YAML syntax in configuration file\n   - Review default values if config file is not found\n\n4. **Metrics Not Appearing**:\n   - Verify worker is running (metrics are exposed automatically)\n   - Check metrics endpoint is accessible: http://127.0.0.1:8004/metrics\n   - Ensure Prometheus is configured to scrape the endpoint\n   - Check for metric name sanitization issues\n\n5. **Dashboard Not Loading**:\n   - Verify Grafana is running\n   - Check dashboard URL is correct\n   - Ensure Prometheus data source is configured\n"
  },
  {
    "path": "cmd/samples/advanced/autoscaling-monitoring/activities.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"math/rand\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/zap\"\n)\n\nconst (\n\tloadGenerationActivityName = \"loadGenerationActivity\"\n)\n\n// LoadGenerationActivity simulates work that can be scaled\n// It includes random delays to simulate real-world processing time\nfunc LoadGenerationActivity(ctx context.Context, taskID int, minProcessingTime, maxProcessingTime int) error {\n\tstartTime := time.Now()\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"Load generation activity started\", zap.Int(\"taskID\", taskID))\n\n\t// Simulate variable processing time using configuration values\n\tprocessingTime := time.Duration(rand.Intn(maxProcessingTime - minProcessingTime) + minProcessingTime) * time.Millisecond\n\ttime.Sleep(processingTime)\n\n\tduration := time.Since(startTime)\n\n\tlogger.Info(\"Load generation activity completed\",\n\t\tzap.Int(\"taskID\", taskID),\n\t\tzap.Duration(\"processingTime\", processingTime),\n\t\tzap.Duration(\"totalDuration\", duration))\n\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/samples/advanced/autoscaling-monitoring/config/autoscaling.yaml",
    "content": "# Configuration for autoscaling monitoring sample\ndomain: \"default\"\nservice: \"cadence-frontend\"\nhost: \"localhost:7833\"\n\n# Prometheus configuration for metrics collection\nprometheus:\n  listenAddress: \"127.0.0.1:8004\"\n\n# Autoscaling configuration\n# These settings control the worker's concurrency and autoscaling behavior\nautoscaling:\n  # Worker autoscaling settings\n  pollerMinCount: 2\n  pollerMaxCount: 8\n  pollerInitCount: 4\n  \n  # Worker load simulation settings\n  loadGeneration:\n    # Workflow-level settings\n    workflows: 10             # Number of workflows to start\n    workflowDelay: 1000       # Delay between starting workflows (milliseconds)\n    \n    # Activity-level settings (per workflow)\n    activitiesPerWorkflow: 30 # Number of activities per workflow\n    batchDelay: 750         # Delay between activity batches within workflow (milliseconds)\n    \n    # Activity processing time range (milliseconds)\n    minProcessingTime: 1000\n    maxProcessingTime: 6000\n"
  },
  {
    "path": "cmd/samples/advanced/autoscaling-monitoring/config.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n\t\"github.com/uber-go/tally/prometheus\"\n\t\"gopkg.in/yaml.v3\"\n)\n\n// AutoscalingConfiguration uses a flattened structure to avoid embedded struct issues\ntype AutoscalingConfiguration struct {\n\t// Base configuration fields (explicit, not embedded)\n\tDomainName      string                    `yaml:\"domain\"`\n\tServiceName     string                    `yaml:\"service\"`\n\tHostNameAndPort string                    `yaml:\"host\"`\n\tPrometheus      *prometheus.Configuration `yaml:\"prometheus\"`\n\n\t// Autoscaling-specific fields\n\tAutoscaling AutoscalingSettings `yaml:\"autoscaling\"`\n}\n\n// AutoscalingSettings contains the autoscaling configuration\ntype AutoscalingSettings struct {\n\t// Worker autoscaling settings\n\tPollerMinCount  int `yaml:\"pollerMinCount\"`\n\tPollerMaxCount  int `yaml:\"pollerMaxCount\"`\n\tPollerInitCount int `yaml:\"pollerInitCount\"`\n\n\t// Load generation settings\n\tLoadGeneration LoadGenerationSettings `yaml:\"loadGeneration\"`\n}\n\n// LoadGenerationSettings contains the load generation configuration\ntype LoadGenerationSettings struct {\n\t// Workflow-level settings\n\tWorkflows     int `yaml:\"workflows\"`\n\tWorkflowDelay int `yaml:\"workflowDelay\"`\n\n\t// Activity-level settings (per workflow)\n\tActivitiesPerWorkflow int `yaml:\"activitiesPerWorkflow\"`\n\tBatchDelay            int `yaml:\"batchDelay\"`\n\tMinProcessingTime     int `yaml:\"minProcessingTime\"`\n\tMaxProcessingTime     int `yaml:\"maxProcessingTime\"`\n}\n\n// Default values as constants for easy maintenance\nconst (\n\tDefaultDomainName      = \"default\"\n\tDefaultServiceName     = \"cadence-frontend\"\n\tDefaultHostNameAndPort = \"localhost:7833\"\n\tDefaultPrometheusAddr  = \"127.0.0.1:8004\"\n\n\tDefaultPollerMinCount  = 2\n\tDefaultPollerMaxCount  = 8\n\tDefaultPollerInitCount = 4\n\n\tDefaultWorkflows             = 10\n\tDefaultWorkflowDelay         = 1000\n\tDefaultActivitiesPerWorkflow = 30\n\tDefaultBatchDelay            = 2000\n\tDefaultMinProcessingTime     = 1000\n\tDefaultMaxProcessingTime     = 6000\n)\n\n// DefaultAutoscalingConfiguration returns default configuration\nfunc DefaultAutoscalingConfiguration() AutoscalingConfiguration {\n\treturn AutoscalingConfiguration{\n\t\tDomainName:      DefaultDomainName,\n\t\tServiceName:     DefaultServiceName,\n\t\tHostNameAndPort: DefaultHostNameAndPort,\n\t\tPrometheus: &prometheus.Configuration{\n\t\t\tListenAddress: DefaultPrometheusAddr,\n\t\t},\n\t\tAutoscaling: AutoscalingSettings{\n\t\t\tPollerMinCount:  DefaultPollerMinCount,\n\t\t\tPollerMaxCount:  DefaultPollerMaxCount,\n\t\t\tPollerInitCount: DefaultPollerInitCount,\n\t\t\tLoadGeneration: LoadGenerationSettings{\n\t\t\t\tWorkflows:             DefaultWorkflows,\n\t\t\t\tWorkflowDelay:         DefaultWorkflowDelay,\n\t\t\t\tActivitiesPerWorkflow: DefaultActivitiesPerWorkflow,\n\t\t\t\tBatchDelay:            DefaultBatchDelay,\n\t\t\t\tMinProcessingTime:     DefaultMinProcessingTime,\n\t\t\t\tMaxProcessingTime:     DefaultMaxProcessingTime,\n\t\t\t},\n\t\t},\n\t}\n}\n\n// loadConfiguration loads the autoscaling configuration from file\nfunc loadConfiguration(configFile string) AutoscalingConfiguration {\n\t// Start with defaults\n\tconfig := DefaultAutoscalingConfiguration()\n\n\t// Read config file\n\tconfigData, err := os.ReadFile(configFile)\n\tif err != nil {\n\t\tfmt.Printf(\"Failed to read config file: %v, using defaults\\n\", err)\n\t\treturn config\n\t}\n\n\t// Unmarshal into the config struct\n\tif err := yaml.Unmarshal(configData, &config); err != nil {\n\t\tfmt.Printf(\"Error parsing configuration: %v, using defaults\\n\", err)\n\t\treturn DefaultAutoscalingConfiguration()\n\t}\n\n\t// Apply defaults for any missing fields\n\tconfig.applyDefaults()\n\n\treturn config\n}\n\n// applyDefaults ensures all fields have sensible values\nfunc (c *AutoscalingConfiguration) applyDefaults() {\n\t// Base configuration defaults\n\tif c.DomainName == \"\" {\n\t\tc.DomainName = DefaultDomainName\n\t}\n\tif c.ServiceName == \"\" {\n\t\tc.ServiceName = DefaultServiceName\n\t}\n\tif c.HostNameAndPort == \"\" {\n\t\tc.HostNameAndPort = DefaultHostNameAndPort\n\t}\n\tif c.Prometheus == nil {\n\t\tc.Prometheus = &prometheus.Configuration{\n\t\t\tListenAddress: DefaultPrometheusAddr,\n\t\t}\n\t}\n\n\t// Autoscaling defaults\n\tif c.Autoscaling.PollerMinCount == 0 {\n\t\tc.Autoscaling.PollerMinCount = DefaultPollerMinCount\n\t}\n\tif c.Autoscaling.PollerMaxCount == 0 {\n\t\tc.Autoscaling.PollerMaxCount = DefaultPollerMaxCount\n\t}\n\tif c.Autoscaling.PollerInitCount == 0 {\n\t\tc.Autoscaling.PollerInitCount = DefaultPollerInitCount\n\t}\n\n\t// Load generation defaults\n\tif c.Autoscaling.LoadGeneration.Workflows == 0 {\n\t\tc.Autoscaling.LoadGeneration.Workflows = DefaultWorkflows\n\t}\n\tif c.Autoscaling.LoadGeneration.WorkflowDelay == 0 {\n\t\tc.Autoscaling.LoadGeneration.WorkflowDelay = DefaultWorkflowDelay\n\t}\n\tif c.Autoscaling.LoadGeneration.ActivitiesPerWorkflow == 0 {\n\t\tc.Autoscaling.LoadGeneration.ActivitiesPerWorkflow = DefaultActivitiesPerWorkflow\n\t}\n\tif c.Autoscaling.LoadGeneration.BatchDelay == 0 {\n\t\tc.Autoscaling.LoadGeneration.BatchDelay = DefaultBatchDelay\n\t}\n\tif c.Autoscaling.LoadGeneration.MinProcessingTime == 0 {\n\t\tc.Autoscaling.LoadGeneration.MinProcessingTime = DefaultMinProcessingTime\n\t}\n\tif c.Autoscaling.LoadGeneration.MaxProcessingTime == 0 {\n\t\tc.Autoscaling.LoadGeneration.MaxProcessingTime = DefaultMaxProcessingTime\n\t}\n}\n\n// ToCommonConfiguration converts to the common.Configuration type for compatibility\nfunc (c *AutoscalingConfiguration) ToCommonConfiguration() common.Configuration {\n\treturn common.Configuration{\n\t\tDomainName:      c.DomainName,\n\t\tServiceName:     c.ServiceName,\n\t\tHostNameAndPort: c.HostNameAndPort,\n\t\tPrometheus:      c.Prometheus,\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/advanced/autoscaling-monitoring/config_test.go",
    "content": "package main\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// Test the improved configuration loader for regressions\nfunc TestLoadConfiguration_SuccessfulLoading(t *testing.T) {\n\t// Create a temporary configuration file with all fields populated\n\tconfigContent := `\ndomain: \"test-domain\"\nservice: \"test-service\"\nhost: \"test-host:7833\"\nprometheus:\n  listenAddress: \"127.0.0.1:9000\"\nautoscaling:\n  pollerMinCount: 3\n  pollerMaxCount: 10\n  pollerInitCount: 5\n  loadGeneration:\n    workflows: 10\n    workflowDelay: 1000\n    activitiesPerWorkflow: 30\n    batchDelay: 5\n    minProcessingTime: 2000\n    maxProcessingTime: 8000\n`\n\n\t// Create temporary file\n\ttmpFile, err := os.CreateTemp(\"\", \"test-config-*.yaml\")\n\trequire.NoError(t, err)\n\tdefer os.Remove(tmpFile.Name())\n\n\t_, err = tmpFile.WriteString(configContent)\n\trequire.NoError(t, err)\n\ttmpFile.Close()\n\n\t// Load configuration\n\tconfig := loadConfiguration(tmpFile.Name())\n\n\t// Validate all fields are populated correctly\n\tassert.Equal(t, \"test-domain\", config.DomainName)\n\tassert.Equal(t, \"test-service\", config.ServiceName)\n\tassert.Equal(t, \"test-host:7833\", config.HostNameAndPort)\n\trequire.NotNil(t, config.Prometheus)\n\tassert.Equal(t, \"127.0.0.1:9000\", config.Prometheus.ListenAddress)\n\tassert.Equal(t, 3, config.Autoscaling.PollerMinCount)\n\tassert.Equal(t, 10, config.Autoscaling.PollerMaxCount)\n\tassert.Equal(t, 5, config.Autoscaling.PollerInitCount)\n\tassert.Equal(t, 10, config.Autoscaling.LoadGeneration.Workflows)\n\tassert.Equal(t, 1000, config.Autoscaling.LoadGeneration.WorkflowDelay)\n\tassert.Equal(t, 30, config.Autoscaling.LoadGeneration.ActivitiesPerWorkflow)\n\tassert.Equal(t, 5, config.Autoscaling.LoadGeneration.BatchDelay)\n\tassert.Equal(t, 2000, config.Autoscaling.LoadGeneration.MinProcessingTime)\n\tassert.Equal(t, 8000, config.Autoscaling.LoadGeneration.MaxProcessingTime)\n}\n\nfunc TestLoadConfiguration_MissingFileFallback(t *testing.T) {\n\t// Use a non-existent file path\n\tconfig := loadConfiguration(\"/non/existent/path/config.yaml\")\n\n\t// Validate that default configuration is returned\n\tassert.Equal(t, DefaultDomainName, config.DomainName)\n\tassert.Equal(t, DefaultServiceName, config.ServiceName)\n\tassert.Equal(t, DefaultHostNameAndPort, config.HostNameAndPort)\n\tassert.Equal(t, DefaultPollerMinCount, config.Autoscaling.PollerMinCount)\n\tassert.Equal(t, DefaultPollerMaxCount, config.Autoscaling.PollerMaxCount)\n\tassert.Equal(t, DefaultPollerInitCount, config.Autoscaling.PollerInitCount)\n\tassert.Equal(t, DefaultWorkflows, config.Autoscaling.LoadGeneration.Workflows)\n\tassert.Equal(t, DefaultWorkflowDelay, config.Autoscaling.LoadGeneration.WorkflowDelay)\n\tassert.Equal(t, DefaultActivitiesPerWorkflow, config.Autoscaling.LoadGeneration.ActivitiesPerWorkflow)\n\tassert.Equal(t, DefaultBatchDelay, config.Autoscaling.LoadGeneration.BatchDelay)\n\tassert.Equal(t, DefaultMinProcessingTime, config.Autoscaling.LoadGeneration.MinProcessingTime)\n\tassert.Equal(t, DefaultMaxProcessingTime, config.Autoscaling.LoadGeneration.MaxProcessingTime)\n}\n\nfunc TestDefaultAutoscalingConfiguration(t *testing.T) {\n\tconfig := DefaultAutoscalingConfiguration()\n\n\t// Validate all default values\n\tassert.Equal(t, DefaultDomainName, config.DomainName)\n\tassert.Equal(t, DefaultServiceName, config.ServiceName)\n\tassert.Equal(t, DefaultHostNameAndPort, config.HostNameAndPort)\n\trequire.NotNil(t, config.Prometheus)\n\tassert.Equal(t, DefaultPrometheusAddr, config.Prometheus.ListenAddress)\n\tassert.Equal(t, DefaultPollerMinCount, config.Autoscaling.PollerMinCount)\n\tassert.Equal(t, DefaultPollerMaxCount, config.Autoscaling.PollerMaxCount)\n\tassert.Equal(t, DefaultPollerInitCount, config.Autoscaling.PollerInitCount)\n\tassert.Equal(t, DefaultWorkflows, config.Autoscaling.LoadGeneration.Workflows)\n\tassert.Equal(t, DefaultWorkflowDelay, config.Autoscaling.LoadGeneration.WorkflowDelay)\n\tassert.Equal(t, DefaultActivitiesPerWorkflow, config.Autoscaling.LoadGeneration.ActivitiesPerWorkflow)\n\tassert.Equal(t, DefaultBatchDelay, config.Autoscaling.LoadGeneration.BatchDelay)\n\tassert.Equal(t, DefaultMinProcessingTime, config.Autoscaling.LoadGeneration.MinProcessingTime)\n\tassert.Equal(t, DefaultMaxProcessingTime, config.Autoscaling.LoadGeneration.MaxProcessingTime)\n}\n\nfunc TestApplyDefaults(t *testing.T) {\n\t// Test with empty configuration\n\tconfig := AutoscalingConfiguration{}\n\tconfig.applyDefaults()\n\n\t// Validate that all defaults are applied\n\tassert.Equal(t, DefaultDomainName, config.DomainName)\n\tassert.Equal(t, DefaultServiceName, config.ServiceName)\n\tassert.Equal(t, DefaultHostNameAndPort, config.HostNameAndPort)\n\trequire.NotNil(t, config.Prometheus)\n\tassert.Equal(t, DefaultPrometheusAddr, config.Prometheus.ListenAddress)\n\tassert.Equal(t, DefaultPollerMinCount, config.Autoscaling.PollerMinCount)\n\tassert.Equal(t, DefaultPollerMaxCount, config.Autoscaling.PollerMaxCount)\n\tassert.Equal(t, DefaultPollerInitCount, config.Autoscaling.PollerInitCount)\n\tassert.Equal(t, DefaultWorkflows, config.Autoscaling.LoadGeneration.Workflows)\n\tassert.Equal(t, DefaultWorkflowDelay, config.Autoscaling.LoadGeneration.WorkflowDelay)\n\tassert.Equal(t, DefaultActivitiesPerWorkflow, config.Autoscaling.LoadGeneration.ActivitiesPerWorkflow)\n\tassert.Equal(t, DefaultBatchDelay, config.Autoscaling.LoadGeneration.BatchDelay)\n\tassert.Equal(t, DefaultMinProcessingTime, config.Autoscaling.LoadGeneration.MinProcessingTime)\n\tassert.Equal(t, DefaultMaxProcessingTime, config.Autoscaling.LoadGeneration.MaxProcessingTime)\n}\n"
  },
  {
    "path": "cmd/samples/advanced/autoscaling-monitoring/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n\t\"github.com/uber-go/tally\"\n\t\"github.com/uber-go/tally/prometheus\"\n\t\"go.uber.org/zap\"\n)\n\nconst (\n\tApplicationName = \"autoscaling-monitoring\"\n)\n\n// findConfigFile finds the config file relative to the executable location\nfunc findConfigFile() string {\n\t// Get the directory where the executable is located\n\texecPath, err := os.Executable()\n\tif err != nil {\n\t\t// Fallback to current working directory if we can't determine executable path\n\t\treturn \"config/autoscaling.yaml\"\n\t}\n\texecDir := filepath.Dir(execPath)\n\n\t// Try to find the config file relative to the executable\n\t// The executable is in bin/, so we need to go up to the repo root and then to the config\n\tconfigPath := filepath.Join(execDir, \"..\", \"cmd\", \"samples\", \"advanced\", \"autoscaling-monitoring\", \"config\", \"autoscaling.yaml\")\n\n\t// Check if the config file exists at this path\n\tif _, err := os.Stat(configPath); err == nil {\n\t\treturn configPath\n\t}\n\n\t// Fallback to the original relative path (for development when running with go run)\n\treturn \"config/autoscaling.yaml\"\n}\n\nfunc main() {\n\t// Parse command line arguments\n\tvar mode string\n\tvar configFile string\n\tflag.StringVar(&mode, \"m\", \"worker\", \"Mode: worker or trigger\")\n\tflag.StringVar(&configFile, \"config\", \"\", \"Path to configuration file\")\n\tflag.Parse()\n\n\t// Load configuration\n\tif configFile == \"\" {\n\t\tconfigFile = findConfigFile()\n\t}\n\tconfig := loadConfiguration(configFile)\n\n\t// Setup common helper with our configuration\n\tvar h common.SampleHelper\n\th.Config = config.ToCommonConfiguration()\n\n\t// Set up logging\n\tlogger, err := zap.NewDevelopment()\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"Failed to setup logger: %v\", err))\n\t}\n\th.Logger = logger\n\n\t// Set up service client using our config\n\th.Builder = common.NewBuilder(logger).\n\t\tSetHostPort(config.HostNameAndPort).\n\t\tSetDomain(config.DomainName)\n\n\tservice, err := h.Builder.BuildServiceClient()\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"Failed to build service client: %v\", err))\n\t}\n\th.Service = service\n\n\t// Set up metrics scope with Tally Prometheus reporter\n\tvar (\n\t\tsafeCharacters  = []rune{'_'}\n\t\tsanitizeOptions = tally.SanitizeOptions{\n\t\t\tNameCharacters: tally.ValidCharacters{\n\t\t\t\tRanges:     tally.AlphanumericRange,\n\t\t\t\tCharacters: safeCharacters,\n\t\t\t},\n\t\t\tKeyCharacters: tally.ValidCharacters{\n\t\t\t\tRanges:     tally.AlphanumericRange,\n\t\t\t\tCharacters: safeCharacters,\n\t\t\t},\n\t\t\tValueCharacters: tally.ValidCharacters{\n\t\t\t\tRanges:     tally.AlphanumericRange,\n\t\t\t\tCharacters: safeCharacters,\n\t\t\t},\n\t\t\tReplacementCharacter: tally.DefaultReplacementCharacter,\n\t\t}\n\t)\n\n\t// Create Prometheus reporter\n\treporter := prometheus.NewReporter(prometheus.Options{})\n\n\t// Create root scope with proper options\n\tscope, closer := tally.NewRootScope(tally.ScopeOptions{\n\t\tTags:            map[string]string{\"service\": \"autoscaling-monitoring\"},\n\t\tSanitizeOptions: &sanitizeOptions,\n\t\tCachedReporter:  reporter,\n\t}, 10)\n\tdefer closer.Close()\n\n\t// Set up metrics scope for helper\n\th.WorkerMetricScope = scope\n\th.ServiceMetricScope = scope\n\n\tswitch mode {\n\tcase \"worker\":\n\t\t// Start metrics server only in worker mode\n\t\tif config.Prometheus != nil {\n\t\t\tgo func() {\n\t\t\t\thttp.Handle(\"/metrics\", reporter.HTTPHandler())\n\t\t\t\tlogger.Info(\"Starting Prometheus metrics server\",\n\t\t\t\t\tzap.String(\"port\", config.Prometheus.ListenAddress))\n\t\t\t\tif err := http.ListenAndServe(config.Prometheus.ListenAddress, nil); err != nil {\n\t\t\t\t\tlogger.Error(\"Failed to start metrics server\", zap.Error(err))\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t\tstartWorkers(&h, &config)\n\tcase \"trigger\":\n\t\tstartWorkflow(&h, &config)\n\tdefault:\n\t\tfmt.Printf(\"Unknown mode: %s\\n\", mode)\n\t\tos.Exit(1)\n\t}\n}\n\nfunc startWorkers(h *common.SampleHelper, config *AutoscalingConfiguration) {\n\tstartWorkersWithAutoscaling(h, config)\n}\n\nfunc startWorkflow(h *common.SampleHelper, config *AutoscalingConfiguration) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              fmt.Sprintf(\"autoscaling_%s\", uuid.New()),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute * 10,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\n\t// Use configuration values\n\tworkflows := config.Autoscaling.LoadGeneration.Workflows\n\tworkflowDelay := config.Autoscaling.LoadGeneration.WorkflowDelay\n\tactivitiesPerWorkflow := config.Autoscaling.LoadGeneration.ActivitiesPerWorkflow\n\tbatchDelay := config.Autoscaling.LoadGeneration.BatchDelay\n\tminProcessingTime := config.Autoscaling.LoadGeneration.MinProcessingTime\n\tmaxProcessingTime := config.Autoscaling.LoadGeneration.MaxProcessingTime\n\n\t// Start multiple workflows with delays\n\tfor i := 0; i < workflows; i++ {\n\t\tworkflowOptions.ID = fmt.Sprintf(\"autoscaling_%d_%s\", i, uuid.New())\n\t\th.StartWorkflow(workflowOptions, autoscalingWorkflowName, activitiesPerWorkflow, batchDelay, minProcessingTime, maxProcessingTime)\n\n\t\t// Add delay between workflows (except for the last one)\n\t\tif i < workflows-1 {\n\t\t\ttime.Sleep(time.Duration(workflowDelay) * time.Millisecond)\n\t\t}\n\t}\n\n\tfmt.Printf(\"Started %d autoscaling workflows with %d activities each\\n\", workflows, activitiesPerWorkflow)\n\tfmt.Println(\"Monitor the worker performance and autoscaling behavior in Grafana:\")\n\tfmt.Println(\"http://localhost:3000/d/dehkspwgabvuoc/cadence-client\")\n}\n"
  },
  {
    "path": "cmd/samples/advanced/autoscaling-monitoring/worker_config.go",
    "content": "package main\n\nimport (\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// startWorkersWithAutoscaling starts workers with autoscaling configuration\nfunc startWorkersWithAutoscaling(h *common.SampleHelper, config *AutoscalingConfiguration) {\n\t// Configure worker options with autoscaling-friendly settings from config\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t\tAutoScalerOptions: worker.AutoScalerOptions{\n\t\t\tEnabled:         true,\n\t\t\tPollerMinCount:  config.Autoscaling.PollerMinCount,\n\t\t\tPollerMaxCount:  config.Autoscaling.PollerMaxCount,\n\t\t\tPollerInitCount: config.Autoscaling.PollerInitCount,\n\t\t},\n\t\tFeatureFlags: client.FeatureFlags{\n\t\t\tWorkflowExecutionAlreadyCompletedErrorEnabled: true,\n\t\t},\n\t}\n\n\th.Logger.Info(\"Starting workers with autoscaling configuration\",\n\t\tzap.Bool(\"AutoScalerEnabled\", workerOptions.AutoScalerOptions.Enabled),\n\t\tzap.Int(\"PollerMinCount\", workerOptions.AutoScalerOptions.PollerMinCount),\n\t\tzap.Int(\"PollerMaxCount\", workerOptions.AutoScalerOptions.PollerMaxCount),\n\t\tzap.Int(\"PollerInitCount\", workerOptions.AutoScalerOptions.PollerInitCount))\n\n\t// Use worker.NewV2 for autoscaling support\n\tw, err := worker.NewV2(h.Service, h.Config.DomainName, ApplicationName, workerOptions)\n\tif err != nil {\n\t\th.Logger.Fatal(\"Failed to create worker with autoscaling\", zap.Error(err))\n\t}\n\n\t// Register workflows and activities\n\tregisterWorkflowAndActivityForAutoscaling(w)\n\n\t// Start the worker\n\terr = w.Run()\n\tif err != nil {\n\t\th.Logger.Fatal(\"Failed to run worker\", zap.Error(err))\n\t}\n}\n\n// registerWorkflowAndActivityForAutoscaling registers the workflow and activities\nfunc registerWorkflowAndActivityForAutoscaling(w worker.Worker) {\n\tw.RegisterWorkflowWithOptions(AutoscalingWorkflow, workflow.RegisterOptions{Name: autoscalingWorkflowName})\n\tw.RegisterActivityWithOptions(LoadGenerationActivity, activity.RegisterOptions{Name: loadGenerationActivityName})\n}\n"
  },
  {
    "path": "cmd/samples/advanced/autoscaling-monitoring/workflow.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\nconst (\n\tautoscalingWorkflowName = \"autoscalingWorkflow\"\n)\n\n// AutoscalingWorkflow demonstrates a workflow that can generate load\n// to test worker poller autoscaling\nfunc AutoscalingWorkflow(ctx workflow.Context, activitiesPerWorkflow int, batchDelay int, minProcessingTime, maxProcessingTime int) error {\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"Autoscaling workflow started\", zap.Int(\"activitiesPerWorkflow\", activitiesPerWorkflow))\n\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute * 20,\n\t\tStartToCloseTimeout:    time.Minute * 20,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\t// Generate load by executing activities in parallel\n\tvar futures []workflow.Future\n\n\t// Execute activities in batches to create varying load\n\tfor i := 0; i < activitiesPerWorkflow; i++ {\n\t\tfuture := workflow.ExecuteActivity(ctx, LoadGenerationActivity, i, minProcessingTime, maxProcessingTime)\n\t\tfutures = append(futures, future)\n\n\t\t// Add some delay between batches to simulate real-world patterns\n\t\t// Use batch delay from configuration\n\t\tif i > 0 && i % 10 == 0 {\n\t\t\tworkflow.Sleep(ctx, time.Duration(batchDelay)*time.Millisecond)\n\t\t}\n\t}\n\n\t// Wait for all activities to complete\n\tfor i, future := range futures {\n\t\tvar result error\n\t\tif err := future.Get(ctx, &result); err != nil {\n\t\t\tlogger.Error(\"Activity failed\", zap.Int(\"taskID\", i), zap.Error(err))\n\t\t\treturn err\n\t\t}\n\t}\n\n\tlogger.Info(\"Autoscaling workflow completed\", zap.Int(\"totalActivities\", len(futures)))\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/samples/common/factory.go",
    "content": "package common\n\nimport (\n\t\"errors\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"github.com/uber-go/tally\"\n\tapiv1 \"github.com/uber/cadence-idl/go/proto/api/v1\"\n\t\"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/compatibility\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/yarpc\"\n\t\"go.uber.org/yarpc/transport/grpc\"\n\t\"go.uber.org/zap\"\n)\n\nconst (\n\t_cadenceClientName      = \"cadence-client\"\n\t_cadenceFrontendService = \"cadence-frontend\"\n)\n\n// WorkflowClientBuilder build client to cadence service\ntype WorkflowClientBuilder struct {\n\thostPort       string\n\tdispatcher     *yarpc.Dispatcher\n\tdomain         string\n\tclientIdentity string\n\tmetricsScope   tally.Scope\n\tLogger         *zap.Logger\n\tctxProps       []workflow.ContextPropagator\n\tdataConverter  encoded.DataConverter\n\ttracer         opentracing.Tracer\n}\n\n// NewBuilder creates a new WorkflowClientBuilder\nfunc NewBuilder(logger *zap.Logger) *WorkflowClientBuilder {\n\treturn &WorkflowClientBuilder{\n\t\tLogger: logger,\n\t}\n}\n\n// SetHostPort sets the hostport for the builder\nfunc (b *WorkflowClientBuilder) SetHostPort(hostport string) *WorkflowClientBuilder {\n\tb.hostPort = hostport\n\treturn b\n}\n\n// SetDomain sets the domain for the builder\nfunc (b *WorkflowClientBuilder) SetDomain(domain string) *WorkflowClientBuilder {\n\tb.domain = domain\n\treturn b\n}\n\n// SetClientIdentity sets the identity for the builder\nfunc (b *WorkflowClientBuilder) SetClientIdentity(identity string) *WorkflowClientBuilder {\n\tb.clientIdentity = identity\n\treturn b\n}\n\n// SetMetricsScope sets the metrics scope for the builder\nfunc (b *WorkflowClientBuilder) SetMetricsScope(metricsScope tally.Scope) *WorkflowClientBuilder {\n\tb.metricsScope = metricsScope\n\treturn b\n}\n\n// SetDispatcher sets the dispatcher for the builder\nfunc (b *WorkflowClientBuilder) SetDispatcher(dispatcher *yarpc.Dispatcher) *WorkflowClientBuilder {\n\tb.dispatcher = dispatcher\n\treturn b\n}\n\n// SetContextPropagators sets the context propagators for the builder\nfunc (b *WorkflowClientBuilder) SetContextPropagators(ctxProps []workflow.ContextPropagator) *WorkflowClientBuilder {\n\tb.ctxProps = ctxProps\n\treturn b\n}\n\n// SetDataConverter sets the data converter for the builder\nfunc (b *WorkflowClientBuilder) SetDataConverter(dataConverter encoded.DataConverter) *WorkflowClientBuilder {\n\tb.dataConverter = dataConverter\n\treturn b\n}\n\n// SetTracer sets the tracer for the builder\nfunc (b *WorkflowClientBuilder) SetTracer(tracer opentracing.Tracer) *WorkflowClientBuilder {\n\tb.tracer = tracer\n\treturn b\n}\n\n// BuildCadenceClient builds a client to cadence service\nfunc (b *WorkflowClientBuilder) BuildCadenceClient() (client.Client, error) {\n\tservice, err := b.BuildServiceClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn client.NewClient(\n\t\tservice,\n\t\tb.domain,\n\t\t&client.Options{\n\t\t\tIdentity:           b.clientIdentity,\n\t\t\tMetricsScope:       b.metricsScope,\n\t\t\tDataConverter:      b.dataConverter,\n\t\t\tContextPropagators: b.ctxProps,\n\t\t\tTracer:             b.tracer,\n\t\t\tFeatureFlags: client.FeatureFlags{\n\t\t\t\tWorkflowExecutionAlreadyCompletedErrorEnabled: true,\n\t\t\t},\n\t\t}), nil\n}\n\n// BuildCadenceDomainClient builds a domain client to cadence service\nfunc (b *WorkflowClientBuilder) BuildCadenceDomainClient() (client.DomainClient, error) {\n\tservice, err := b.BuildServiceClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn client.NewDomainClient(\n\t\tservice,\n\t\t&client.Options{\n\t\t\tIdentity:           b.clientIdentity,\n\t\t\tMetricsScope:       b.metricsScope,\n\t\t\tContextPropagators: b.ctxProps,\n\t\t\tFeatureFlags: client.FeatureFlags{\n\t\t\t\tWorkflowExecutionAlreadyCompletedErrorEnabled: true,\n\t\t\t},\n\t\t},\n\t), nil\n}\n\n// BuildServiceClient builds a rpc service client to cadence service\nfunc (b *WorkflowClientBuilder) BuildServiceClient() (workflowserviceclient.Interface, error) {\n\tif err := b.build(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif b.dispatcher == nil {\n\t\tb.Logger.Fatal(\"No RPC dispatcher provided to create a connection to Cadence Service\")\n\t}\n\n\tclientConfig := b.dispatcher.ClientConfig(_cadenceFrontendService)\n\treturn compatibility.NewThrift2ProtoAdapter(\n\t\tapiv1.NewDomainAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkflowAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkerAPIYARPCClient(clientConfig),\n\t\tapiv1.NewVisibilityAPIYARPCClient(clientConfig),\n\t), nil\n}\n\nfunc (b *WorkflowClientBuilder) build() error {\n\tif b.dispatcher != nil {\n\t\treturn nil\n\t}\n\n\tif len(b.hostPort) == 0 {\n\t\treturn errors.New(\"HostPort is empty\")\n\t}\n\n\tb.Logger.Debug(\"Creating RPC dispatcher outbound\",\n\t\tzap.String(\"ServiceName\", _cadenceFrontendService),\n\t\tzap.String(\"HostPort\", b.hostPort))\n\n\tb.dispatcher = yarpc.NewDispatcher(yarpc.Config{\n\t\tName: _cadenceClientName,\n\t\tOutbounds: yarpc.Outbounds{\n\t\t\t_cadenceFrontendService: {Unary: grpc.NewTransport().NewSingleOutbound(b.hostPort)},\n\t\t},\n\t})\n\n\tif b.dispatcher != nil {\n\t\tif err := b.dispatcher.Start(); err != nil {\n\t\t\tb.Logger.Fatal(\"Failed to create outbound transport channel: %v\", zap.Error(err))\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/samples/common/sample_helper.go",
    "content": "package common\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"time\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"go.uber.org/cadence/.gen/go/shared\"\n\n\tprom \"github.com/m3db/prometheus_client_golang/prometheus\"\n\t\"github.com/uber-go/tally\"\n\t\"github.com/uber-go/tally/prometheus\"\n\t\"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n\t\"gopkg.in/yaml.v2\"\n)\n\nconst (\n\tdefaultConfigFile = \"config/development.yaml\"\n)\n\ntype (\n\t// SampleHelper class for workflow sample helper.\n\tSampleHelper struct {\n\t\tService            workflowserviceclient.Interface\n\t\tWorkerMetricScope  tally.Scope\n\t\tServiceMetricScope tally.Scope\n\t\tLogger             *zap.Logger\n\t\tConfig             Configuration\n\t\tBuilder            *WorkflowClientBuilder\n\t\tDataConverter      encoded.DataConverter\n\t\tCtxPropagators     []workflow.ContextPropagator\n\t\tworkflowRegistries []registryOption\n\t\tactivityRegistries []registryOption\n\t\tTracer             opentracing.Tracer\n\n\t\tconfigFile string\n\t}\n\n\t// Configuration for running samples.\n\tConfiguration struct {\n\t\tDomainName      string                    `yaml:\"domain\"`\n\t\tServiceName     string                    `yaml:\"service\"`\n\t\tHostNameAndPort string                    `yaml:\"host\"`\n\t\tPrometheus      *prometheus.Configuration `yaml:\"prometheus\"`\n\t}\n\n\tregistryOption struct {\n\t\tregistry interface{}\n\t\talias    string\n\t}\n)\n\nvar (\n\tsafeCharacters = []rune{'_'}\n\n\tsanitizeOptions = tally.SanitizeOptions{\n\t\tNameCharacters: tally.ValidCharacters{\n\t\t\tRanges:     tally.AlphanumericRange,\n\t\t\tCharacters: safeCharacters,\n\t\t},\n\t\tKeyCharacters: tally.ValidCharacters{\n\t\t\tRanges:     tally.AlphanumericRange,\n\t\t\tCharacters: safeCharacters,\n\t\t},\n\t\tValueCharacters: tally.ValidCharacters{\n\t\t\tRanges:     tally.AlphanumericRange,\n\t\t\tCharacters: safeCharacters,\n\t\t},\n\t\tReplacementCharacter: tally.DefaultReplacementCharacter,\n\t}\n)\n\n// SetConfigFile sets the config file path\nfunc (h *SampleHelper) SetConfigFile(configFile string) {\n\th.configFile = configFile\n}\n\n// SetupServiceConfig setup the config for the sample code run\nfunc (h *SampleHelper) SetupServiceConfig() {\n\tif h.Service != nil {\n\t\treturn\n\t}\n\n\tif h.configFile == \"\" {\n\t\th.configFile = defaultConfigFile\n\t}\n\t// Initialize developer config for running samples\n\tconfigData, err := ioutil.ReadFile(h.configFile)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"Failed to log config file: %v, Error: %v\", defaultConfigFile, err))\n\t}\n\n\tif err := yaml.Unmarshal(configData, &h.Config); err != nil {\n\t\tpanic(fmt.Sprintf(\"Error initializing configuration: %v\", err))\n\t}\n\n\t// Initialize logger for running samples\n\tlogger, err := zap.NewDevelopment()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tlogger.Info(\"Logger created.\")\n\th.Logger = logger\n\th.ServiceMetricScope = tally.NoopScope\n\th.WorkerMetricScope = tally.NoopScope\n\n\tif h.Config.Prometheus != nil {\n\t\treporter, err := h.Config.Prometheus.NewReporter(\n\t\t\tprometheus.ConfigurationOptions{\n\t\t\t\tRegistry: prom.NewRegistry(),\n\t\t\t\tOnError: func(err error) {\n\t\t\t\t\tlogger.Warn(\"error in prometheus reporter\", zap.Error(err))\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\th.WorkerMetricScope, _ = tally.NewRootScope(tally.ScopeOptions{\n\t\t\tPrefix:          \"Worker_\",\n\t\t\tTags:            map[string]string{},\n\t\t\tCachedReporter:  reporter,\n\t\t\tSeparator:       prometheus.DefaultSeparator,\n\t\t\tSanitizeOptions: &sanitizeOptions,\n\t\t}, 1*time.Second)\n\n\t\t// NOTE: this must be a different scope with different prefix, otherwise the metric will conflict\n\t\th.ServiceMetricScope, _ = tally.NewRootScope(tally.ScopeOptions{\n\t\t\tPrefix:          \"Service_\",\n\t\t\tTags:            map[string]string{},\n\t\t\tCachedReporter:  reporter,\n\t\t\tSeparator:       prometheus.DefaultSeparator,\n\t\t\tSanitizeOptions: &sanitizeOptions,\n\t\t}, 1*time.Second)\n\t}\n\th.Builder = NewBuilder(logger).\n\t\tSetHostPort(h.Config.HostNameAndPort).\n\t\tSetDomain(h.Config.DomainName).\n\t\tSetMetricsScope(h.ServiceMetricScope).\n\t\tSetDataConverter(h.DataConverter).\n\t\tSetTracer(h.Tracer).\n\t\tSetContextPropagators(h.CtxPropagators)\n\tservice, err := h.Builder.BuildServiceClient()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\th.Service = service\n\n\tdomainClient, _ := h.Builder.BuildCadenceDomainClient()\n\t_, err = domainClient.Describe(context.Background(), h.Config.DomainName)\n\tif err != nil {\n\t\tlogger.Info(\"Domain doesn't exist\", zap.String(\"Domain\", h.Config.DomainName), zap.Error(err))\n\t} else {\n\t\tlogger.Info(\"Domain successfully registered.\", zap.String(\"Domain\", h.Config.DomainName))\n\t}\n\n\th.workflowRegistries = make([]registryOption, 0, 1)\n\th.activityRegistries = make([]registryOption, 0, 1)\n}\n\n// StartWorkflow starts a workflow\nfunc (h *SampleHelper) StartWorkflow(\n\toptions client.StartWorkflowOptions,\n\tworkflow interface{},\n\targs ...interface{},\n) *workflow.Execution {\n\treturn h.StartWorkflowWithCtx(context.Background(), options, workflow, args...)\n}\n\n// StartWorkflowWithCtx starts a workflow with the provided context\nfunc (h *SampleHelper) StartWorkflowWithCtx(\n\tctx context.Context,\n\toptions client.StartWorkflowOptions,\n\tworkflow interface{},\n\targs ...interface{},\n) *workflow.Execution {\n\tworkflowClient, err := h.Builder.BuildCadenceClient()\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to build cadence client.\", zap.Error(err))\n\t\tpanic(err)\n\t}\n\n\twe, err := workflowClient.StartWorkflow(ctx, options, workflow, args...)\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to create workflow\", zap.Error(err))\n\t\tpanic(\"Failed to create workflow.\")\n\t} else {\n\t\th.Logger.Info(\"Started Workflow\", zap.String(\"WorkflowID\", we.ID), zap.String(\"RunID\", we.RunID))\n\t\treturn we\n\t}\n}\n\n// SignalWithStartWorkflowWithCtx signals workflow and starts it if it's not yet started\nfunc (h *SampleHelper) SignalWithStartWorkflowWithCtx(ctx context.Context, workflowID string, signalName string, signalArg interface{},\n\toptions client.StartWorkflowOptions, workflow interface{}, workflowArgs ...interface{}) *workflow.Execution {\n\tworkflowClient, err := h.Builder.BuildCadenceClient()\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to build cadence client.\", zap.Error(err))\n\t\tpanic(err)\n\t}\n\n\twe, err := workflowClient.SignalWithStartWorkflow(ctx, workflowID, signalName, signalArg, options, workflow, workflowArgs...)\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to signal with start workflow\", zap.Error(err))\n\t\tpanic(\"Failed to signal with start workflow.\")\n\n\t} else {\n\t\th.Logger.Info(\"Signaled and started Workflow\", zap.String(\"WorkflowID\", we.ID), zap.String(\"RunID\", we.RunID))\n\t}\n\treturn we\n}\n\nfunc (h *SampleHelper) RegisterWorkflow(workflow interface{}) {\n\th.RegisterWorkflowWithAlias(workflow, \"\")\n}\n\nfunc (h *SampleHelper) RegisterWorkflowWithAlias(workflow interface{}, alias string) {\n\tregistryOption := registryOption{\n\t\tregistry: workflow,\n\t\talias:    alias,\n\t}\n\th.workflowRegistries = append(h.workflowRegistries, registryOption)\n}\n\nfunc (h *SampleHelper) RegisterActivity(activity interface{}) {\n\th.RegisterActivityWithAlias(activity, \"\")\n}\n\nfunc (h *SampleHelper) RegisterActivityWithAlias(activity interface{}, alias string) {\n\tregistryOption := registryOption{\n\t\tregistry: activity,\n\t\talias:    alias,\n\t}\n\th.activityRegistries = append(h.activityRegistries, registryOption)\n}\n\n// StartWorkers starts workflow worker and activity worker based on configured options.\nfunc (h *SampleHelper) StartWorkers(domainName string, groupName string, options worker.Options) worker.Worker {\n\tworker := worker.New(h.Service, domainName, groupName, options)\n\th.registerWorkflowAndActivity(worker)\n\n\terr := worker.Start()\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to start workers.\", zap.Error(err))\n\t\tpanic(\"Failed to start workers\")\n\t}\n\n\treturn worker\n}\n\nfunc (h *SampleHelper) QueryWorkflow(workflowID, runID, queryType string, args ...interface{}) {\n\tworkflowClient, err := h.Builder.BuildCadenceClient()\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to build cadence client.\", zap.Error(err))\n\t\tpanic(err)\n\t}\n\n\tresp, err := workflowClient.QueryWorkflow(context.Background(), workflowID, runID, queryType, args...)\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to query workflow\", zap.Error(err))\n\t\tpanic(\"Failed to query workflow.\")\n\t}\n\tvar result interface{}\n\tif err := resp.Get(&result); err != nil {\n\t\th.Logger.Error(\"Failed to decode query result\", zap.Error(err))\n\t}\n\th.Logger.Info(\"Received query result\", zap.Any(\"Result\", result))\n}\n\nfunc (h *SampleHelper) ConsistentQueryWorkflow(\n\tvaluePtr interface{},\n\tworkflowID, runID, queryType string,\n\targs ...interface{},\n) error {\n\tworkflowClient, err := h.Builder.BuildCadenceClient()\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to build cadence client.\", zap.Error(err))\n\t\tpanic(err)\n\t}\n\n\tresp, err := workflowClient.QueryWorkflowWithOptions(context.Background(),\n\t\t&client.QueryWorkflowWithOptionsRequest{\n\t\t\tWorkflowID:            workflowID,\n\t\t\tRunID:                 runID,\n\t\t\tQueryType:             queryType,\n\t\t\tQueryConsistencyLevel: shared.QueryConsistencyLevelStrong.Ptr(),\n\t\t\tArgs:                  args,\n\t\t})\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to query workflow\", zap.Error(err))\n\t\tpanic(\"Failed to query workflow.\")\n\t}\n\tif err := resp.QueryResult.Get(&valuePtr); err != nil {\n\t\th.Logger.Error(\"Failed to decode query result\", zap.Error(err))\n\t}\n\th.Logger.Info(\"Received consistent query result.\", zap.Any(\"Result\", valuePtr))\n\treturn err\n}\n\nfunc (h *SampleHelper) SignalWorkflow(workflowID, signal string, data interface{}) {\n\tworkflowClient, err := h.Builder.BuildCadenceClient()\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to build cadence client.\", zap.Error(err))\n\t\tpanic(err)\n\t}\n\n\terr = workflowClient.SignalWorkflow(context.Background(), workflowID, \"\", signal, data)\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to signal workflow\", zap.Error(err))\n\t\tpanic(\"Failed to signal workflow.\")\n\t}\n}\n\nfunc (h *SampleHelper) CancelWorkflow(workflowID string) {\n\tworkflowClient, err := h.Builder.BuildCadenceClient()\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to build cadence client.\", zap.Error(err))\n\t\tpanic(err)\n\t}\n\n\terr = workflowClient.CancelWorkflow(context.Background(), workflowID, \"\")\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to cancel workflow\", zap.Error(err))\n\t\tpanic(\"Failed to cancel workflow.\")\n\t}\n}\n\nfunc (h *SampleHelper) registerWorkflowAndActivity(worker worker.Worker) {\n\tfor _, w := range h.workflowRegistries {\n\t\tif len(w.alias) == 0 {\n\t\t\tworker.RegisterWorkflow(w.registry)\n\t\t} else {\n\t\t\tworker.RegisterWorkflowWithOptions(w.registry, workflow.RegisterOptions{Name: w.alias})\n\t\t}\n\t}\n\tfor _, act := range h.activityRegistries {\n\t\tif len(act.alias) == 0 {\n\t\t\tworker.RegisterActivity(act.registry)\n\t\t} else {\n\t\t\tworker.RegisterActivityWithOptions(act.registry, activity.RegisterOptions{Name: act.alias})\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/common/util.go",
    "content": "package common\n\n// StringPtr returns pointer to a string\nfunc StringPtr(v string) *string {\n\treturn &v\n}\n\n// Int32Ptr returns pointer to a int32\nfunc Int32Ptr(v int32) *int32 {\n\treturn &v\n}\n\n// Int64Ptr returns pointer to a int64\nfunc Int64Ptr(v int64) *int64 {\n\treturn &v\n}\n"
  },
  {
    "path": "cmd/samples/cron/cron_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * This cron sample workflow will schedule job based on given schedule spec. The schedule spec in this sample demo is\n * very simple, but you could have more complicated scheduler logic that meet your needs.\n */\n\nconst (\n\t// timeout for activity task from put in queue to started\n\tactivityScheduleToStartTimeout = time.Second * 10\n\t// timeout for activity from start to complete\n\tactivityStartToCloseTimeout = time.Minute\n\n\t// WorkflowStartToCloseTimeout (from workflow start to workflow close)\n\tWorkflowStartToCloseTimeout = time.Minute * 20\n\t// DecisionTaskStartToCloseTimeout (from decision task started to decision task completed, usually very short)\n\tDecisionTaskStartToCloseTimeout = time.Second * 10\n)\n\n//\n// Cron sample job activity.\n//\nfunc sampleCronActivity(ctx context.Context, beginTime, endTime time.Time) error {\n\tactivity.GetLogger(ctx).Info(\"Cron job running.\", zap.Time(\"beginTime_exclude\", beginTime), zap.Time(\"endTime_include\", endTime))\n\t// ...\n\treturn nil\n}\n\n// SampleCronResult used to return data from one cron run to next cron run.\ntype SampleCronResult struct {\n\tEndTime time.Time\n}\n\n// sampleCronWorkflow workflow decider\nfunc sampleCronWorkflow(ctx workflow.Context) (*SampleCronResult, error) {\n\tworkflow.GetLogger(ctx).Info(\"Cron workflow started.\", zap.Time(\"StartTime\", workflow.Now(ctx)))\n\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: activityScheduleToStartTimeout,\n\t\tStartToCloseTimeout:    activityStartToCloseTimeout,\n\t}\n\tctx1 := workflow.WithActivityOptions(ctx, ao)\n\n\tstartTime := time.Time{} // start from 0 time for first cron job\n\tif workflow.HasLastCompletionResult(ctx) {\n\t\tvar lastResult SampleCronResult\n\t\tif err := workflow.GetLastCompletionResult(ctx, &lastResult); err == nil {\n\t\t\tstartTime = lastResult.EndTime\n\t\t}\n\t}\n\n\tendTime := workflow.Now(ctx)\n\n\terr := workflow.ExecuteActivity(ctx1, sampleCronActivity, startTime, endTime).Get(ctx, nil)\n\n\tif err != nil {\n\t\t// cron job failed. but next cron should continue to be scheduled by Cadence server\n\t\tworkflow.GetLogger(ctx).Error(\"Cron job failed.\", zap.Error(err))\n\t\treturn nil, err\n\t}\n\n\treturn &SampleCronResult{EndTime: endTime}, nil\n}\n"
  },
  {
    "path": "cmd/samples/cron/cron_workflow_test.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/mock\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/testsuite\"\n\t\"go.uber.org/cadence/workflow\"\n)\n\ntype UnitTestSuite struct {\n\tsuite.Suite\n\ttestsuite.WorkflowTestSuite\n\n\tenv *testsuite.TestWorkflowEnvironment\n}\n\nfunc TestUnitTestSuite(t *testing.T) {\n\tsuite.Run(t, new(UnitTestSuite))\n}\n\nfunc (s *UnitTestSuite) SetupTest() {\n\ts.env = s.NewTestWorkflowEnvironment()\n\ts.env.RegisterWorkflow(sampleCronWorkflow)\n\ts.env.RegisterActivity(sampleCronActivity)\n}\n\nfunc (s *UnitTestSuite) TearDownTest() {\n\ts.env.AssertExpectations(s.T())\n}\n\nfunc (s *UnitTestSuite) Test_CronWorkflow() {\n\ttestWorkflow := func(ctx workflow.Context) error {\n\t\tctx1 := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{\n\t\t\tExecutionStartToCloseTimeout: time.Minute * 10,\n\t\t\tCronSchedule:                 \"0 * * * *\", // hourly\n\t\t})\n\n\t\tcronFuture := workflow.ExecuteChildWorkflow(ctx1, sampleCronWorkflow) // cron never stop so this future won't return\n\n\t\t// wait 2 hours for the cron (cron will execute 3 times)\n\t\tworkflow.Sleep(ctx, time.Hour*2)\n\t\ts.False(cronFuture.IsReady())\n\t\treturn nil\n\t}\n\ts.env.RegisterWorkflow(testWorkflow)\n\n\ts.env.OnActivity(sampleCronActivity, mock.Anything, mock.Anything, mock.Anything).Return(nil).Times(3)\n\n\tvar startTimeList, endTimeList []time.Time\n\ts.env.SetOnActivityStartedListener(func(activityInfo *activity.Info, ctx context.Context, args encoded.Values) {\n\t\tvar startTime, endTime time.Time\n\t\terr := args.Get(&startTime, &endTime)\n\t\ts.NoError(err)\n\n\t\tstartTimeList = append(startTimeList, startTime)\n\t\tendTimeList = append(endTimeList, endTime)\n\t})\n\n\tstartTime, _ := time.Parse(time.RFC3339, \"2018-12-20T16:30:00-80:00\")\n\ts.env.SetStartTime(startTime)\n\n\ts.env.ExecuteWorkflow(testWorkflow)\n\n\ts.True(s.env.IsWorkflowCompleted())\n\terr := s.env.GetWorkflowError()\n\ts.NoError(err)\n\n\ts.Equal(3, len(startTimeList))\n\ts.True(startTimeList[0].Equal(time.Time{}))\n\ts.True(endTimeList[0].Equal(startTime))\n\n\ts.True(startTimeList[1].Equal(startTime))\n\ts.True(endTimeList[1].Equal(startTime.Add(time.Minute * 30)))\n\n\ts.True(startTimeList[2].Equal(startTime.Add(time.Minute * 30)))\n\ts.True(endTimeList[2].Equal(startTime.Add(time.Minute * 90)))\n}\n"
  },
  {
    "path": "cmd/samples/cron/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\nconst (\n\t// ApplicationName is the task list for this sample\n\tApplicationName = \"cronGroup\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t\tFeatureFlags: client.FeatureFlags{\n\t\t\tWorkflowExecutionAlreadyCompletedErrorEnabled: true,\n\t\t},\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\n//\n// To start instance of the workflow.\n//\nfunc startWorkflow(h *common.SampleHelper, cron string) {\n\t// This workflow ID can be user business logic identifier as well.\n\tworkflowID := \"cron_\" + uuid.New()\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              workflowID,\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t\tCronSchedule:                    cron,\n\t}\n\th.StartWorkflow(workflowOptions, sampleCronWorkflow)\n}\n\nfunc main() {\n\tvar mode string\n\tvar cron string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.StringVar(&cron, \"cron\", \"* * * * *\", \"Crontab schedule. Default \\\"* * * * *\\\"\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(sampleCronWorkflow)\n\t\th.RegisterActivity(sampleCronActivity)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h, cron)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/dsl/README.md",
    "content": "This sample demonstrates how to implement a DSL workflow. In this sample, we provide 2 sample yaml files each defines a custom workflow that can be processed by this dsl workflow sample code.\n\nSteps to run this sample:\n1) You need a cadence service running. See cmd/samples/README.md for more details.\n2) Run \"./bin/dsl -m worker\" to start workers for dsl workflow.\n3) Run \"./bin/dsl -dslConfig cmd/samples/dsl/workflow1.yaml\" to submit start request for workflow defined in workflow1.yaml file.\n\nNext:\n1) You can replace the dslConfig to workflow2.yaml to see the result.\n2) You can also write your own yaml config to play with it.\n3) You can replace the dummy activities to your own real activities to build real workflow based on this simple dsl workflow.\n"
  },
  {
    "path": "cmd/samples/dsl/activities.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc sampleActivity1(input []string) (string, error) {\n\tname := \"sampleActivity1\"\n\tfmt.Printf(\"Run %s with input %v \\n\", name, input)\n\treturn \"Result_\" + name, nil\n}\n\nfunc sampleActivity2(input []string) (string, error) {\n\tname := \"sampleActivity2\"\n\tfmt.Printf(\"Run %s with input %v \\n\", name, input)\n\treturn \"Result_\" + name, nil\n}\n\nfunc sampleActivity3(input []string) (string, error) {\n\tname := \"sampleActivity3\"\n\tfmt.Printf(\"Run %s with input %v \\n\", name, input)\n\treturn \"Result_\" + name, nil\n}\n\nfunc sampleActivity4(input []string) (string, error) {\n\tname := \"sampleActivity4\"\n\tfmt.Printf(\"Run %s with input %v \\n\", name, input)\n\treturn \"Result_\" + name, nil\n}\n\nfunc sampleActivity5(input []string) (string, error) {\n\tname := \"sampleActivity5\"\n\tfmt.Printf(\"Run %s with input %v \\n\", name, input)\n\treturn \"Result_\" + name, nil\n}\n"
  },
  {
    "path": "cmd/samples/dsl/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\t\"gopkg.in/yaml.v2\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper, w Workflow) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"dsl_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, simpleDSLWorkflow, w)\n}\n\nfunc main() {\n\tvar mode, dslConfig string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.StringVar(&dslConfig, \"dslConfig\", \"cmd/samples/dsl/workflow1.yaml\", \"dslConfig specify the yaml file for the dsl workflow.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(simpleDSLWorkflow)\n\t\th.RegisterActivity(sampleActivity1)\n\t\th.RegisterActivity(sampleActivity2)\n\t\th.RegisterActivity(sampleActivity3)\n\t\th.RegisterActivity(sampleActivity4)\n\t\th.RegisterActivity(sampleActivity5)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\n\t\tdata, err := ioutil.ReadFile(dslConfig)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Sprintf(\"failed to load dsl config file %v\", err))\n\t\t}\n\t\tvar workflow Workflow\n\t\tif err := yaml.Unmarshal(data, &workflow); err != nil {\n\t\t\tpanic(fmt.Sprintf(\"failed to unmarshal dsl config %v\", err))\n\t\t}\n\t\tstartWorkflow(&h, workflow)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/dsl/workflow.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"dslGroup\"\n\ntype (\n\t// Workflow is the type used to express the workflow definition. Variables are a map of valuables. Variables can be\n\t// used as input to Activity.\n\tWorkflow struct {\n\t\tVariables map[string]string\n\t\tRoot      Statement\n\t}\n\n\t// Statement is the building block of dsl workflow. A Statement can be a simple ActivityInvocation or it\n\t// could be a Sequence or Parallel.\n\tStatement struct {\n\t\tActivity *ActivityInvocation\n\t\tSequence *Sequence\n\t\tParallel *Parallel\n\t}\n\n\t// Sequence consist of a collection of Statements that runs in sequential.\n\tSequence struct {\n\t\tElements []*Statement\n\t}\n\n\t// Parallel can be a collection of Statements that runs in parallel.\n\tParallel struct {\n\t\tBranches []*Statement\n\t}\n\n\t// ActivityInvocation is used to express invoking an Activity. The Arguments defined expected arguments as input to\n\t// the Activity, the result specify the name of variable that it will store the result as which can then be used as\n\t// arguments to subsequent ActivityInvocation.\n\tActivityInvocation struct {\n\t\tName      string\n\t\tArguments []string\n\t\tResult    string\n\t}\n\n\texecutable interface {\n\t\texecute(ctx workflow.Context, bindings map[string]string) error\n\t}\n)\n\n// simpleDSLWorkflow workflow decider\nfunc simpleDSLWorkflow(ctx workflow.Context, dslWorkflow Workflow) ([]byte, error) {\n\tbindings := make(map[string]string)\n\tfor k, v := range dslWorkflow.Variables {\n\t\tbindings[k] = v\n\t}\n\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\tlogger := workflow.GetLogger(ctx)\n\n\terr := dslWorkflow.Root.execute(ctx, bindings)\n\tif err != nil {\n\t\tlogger.Error(\"DSL Workflow failed.\", zap.Error(err))\n\t\treturn nil, err\n\t}\n\n\tlogger.Info(\"DSL Workflow completed.\")\n\treturn nil, err\n}\n\nfunc (b *Statement) execute(ctx workflow.Context, bindings map[string]string) error {\n\tif b.Parallel != nil {\n\t\terr := b.Parallel.execute(ctx, bindings)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif b.Sequence != nil {\n\t\terr := b.Sequence.execute(ctx, bindings)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif b.Activity != nil {\n\t\terr := b.Activity.execute(ctx, bindings)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (a ActivityInvocation) execute(ctx workflow.Context, bindings map[string]string) error {\n\tinputParam := makeInput(a.Arguments, bindings)\n\tvar result string\n\terr := workflow.ExecuteActivity(ctx, a.Name, inputParam).Get(ctx, &result)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif a.Result != \"\" {\n\t\tbindings[a.Result] = result\n\t}\n\treturn nil\n}\n\nfunc (s Sequence) execute(ctx workflow.Context, bindings map[string]string) error {\n\tfor _, a := range s.Elements {\n\t\terr := a.execute(ctx, bindings)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (p Parallel) execute(ctx workflow.Context, bindings map[string]string) error {\n\t//\n\t// You can use the context passed in to activity as a way to cancel the activity like standard GO way.\n\t// Cancelling a parent context will cancel all the derived contexts as well.\n\t//\n\n\t// In the parallel block, we want to execute all of them in parallel and wait for all of them.\n\t// if one activity fails then we want to cancel all the rest of them as well.\n\tchildCtx, cancelHandler := workflow.WithCancel(ctx)\n\tselector := workflow.NewSelector(ctx)\n\tvar activityErr error\n\tfor _, s := range p.Branches {\n\t\tf := executeAsync(s, childCtx, bindings)\n\t\tselector.AddFuture(f, func(f workflow.Future) {\n\t\t\terr := f.Get(ctx, nil)\n\t\t\tif err != nil {\n\t\t\t\t// cancel all pending activities\n\t\t\t\tcancelHandler()\n\t\t\t\tactivityErr = err\n\t\t\t}\n\t\t})\n\t}\n\n\tfor i := 0; i < len(p.Branches); i++ {\n\t\tselector.Select(ctx) // this will wait for one branch\n\t\tif activityErr != nil {\n\t\t\treturn activityErr\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc executeAsync(exe executable, ctx workflow.Context, bindings map[string]string) workflow.Future {\n\tfuture, settable := workflow.NewFuture(ctx)\n\tworkflow.Go(ctx, func(ctx workflow.Context) {\n\t\terr := exe.execute(ctx, bindings)\n\t\tsettable.Set(nil, err)\n\t})\n\treturn future\n}\n\nfunc makeInput(argNames []string, argsMap map[string]string) []string {\n\tvar args []string\n\tfor _, arg := range argNames {\n\t\targs = append(args, argsMap[arg])\n\t}\n\treturn args\n}\n"
  },
  {
    "path": "cmd/samples/dsl/workflow1.yaml",
    "content": "# This sample workflow execute 3 steps in sequence.\n# 1) sampleActivity1, takes arg1 as input, and put result as result1.\n# 2) sampleActivity2, takes result1 as input, and put result as result2.\n# 3) sampleActivity3, takes args2 and result2 as input, and put result as result3.\n\nvariables:\n  arg1: value1\n  arg2: value2\n\nroot:\n  sequence:\n    elements:\n     - activity:\n        name: main.sampleActivity1\n        arguments:\n          - arg1\n        result: result1\n     - activity:\n        name: main.sampleActivity2\n        arguments:\n          - result1\n        result: result2\n     - activity:\n        name: main.sampleActivity3\n        arguments:\n          - arg2\n          - result2\n        result: result3\n"
  },
  {
    "path": "cmd/samples/dsl/workflow2.yaml",
    "content": "# This sample workflow execute 3 steps in sequence.\n# 1) activity1, takes arg1 as input, and put result as result1.\n# 2) it runs a parallel block which runs below sequence branches in parallel\n#  2.1) sequence 1\n#    2.1.1) activity2, takes result1 as input, and put result as result2\n#    2.1.2) activity3, takes arg2 and result2 as input, and put result as result3\n#  2.2) sequence 2\n#    2.2.1) activity4, takes result1 as input, and put result as result4\n#    2.2.2) activity5, takes arg3 and result4 as input, and put result as result5\n# 3) activity1, takes result3 and result5 as input, and put result as result6.\n\nvariables:\n  arg1: value1\n  arg2: value2\n  arg3: value3\n\nroot:\n  sequence:\n    elements:\n      - activity:\n         name: main.sampleActivity1\n         arguments:\n           - arg1\n         result: result1\n      - parallel:\n          branches:\n            - sequence:\n                elements:\n                 - activity:\n                    name: main.sampleActivity2\n                    arguments:\n                      - result1\n                    result: result2\n                 - activity:\n                    name: main.sampleActivity3\n                    arguments:\n                      - arg2\n                      - result2\n                    result: result3\n            - sequence:\n                elements:\n                 - activity:\n                    name: main.sampleActivity4\n                    arguments:\n                      - result1\n                    result: result4\n                 - activity:\n                    name: main.sampleActivity5\n                    arguments:\n                      - arg3\n                      - result4\n                    result: result5\n      - activity:\n         name: main.sampleActivity1\n         arguments:\n           - result3\n           - result5\n         result: result6\n"
  },
  {
    "path": "cmd/samples/dsl/workflow_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/testsuite\"\n\t\"go.uber.org/cadence/workflow\"\n)\n\nfunc TestActivitySequenceParallelStatements(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\n\ttests := []struct {\n\t\tname     string\n\t\tfields   Statement\n\t\tbindings map[string]string\n\t\twantErr  bool\n\t}{\n\t\t{\n\t\t\tname: \"Test Activity Invocation\",\n\t\t\tfields: Statement{\n\t\t\t\tActivity: &ActivityInvocation{\n\t\t\t\t\tName:      \"sampleActivity\",\n\t\t\t\t\tArguments: []string{\"var1\", \"var2\"},\n\t\t\t\t\tResult:    \"resultVar\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tbindings: map[string]string{\n\t\t\t\t\"var1\": \"value1\",\n\t\t\t\t\"var2\": \"value2\",\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Test Sequence Execution\",\n\t\t\tfields: Statement{\n\t\t\t\tSequence: &Sequence{\n\t\t\t\t\tElements: []*Statement{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tActivity: &ActivityInvocation{\n\t\t\t\t\t\t\t\tName:      \"sampleActivity\",\n\t\t\t\t\t\t\t\tArguments: []string{\"var1\"},\n\t\t\t\t\t\t\t\tResult:    \"resultVar1\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tActivity: &ActivityInvocation{\n\t\t\t\t\t\t\t\tName:      \"sampleActivity\",\n\t\t\t\t\t\t\t\tArguments: []string{\"var2\"},\n\t\t\t\t\t\t\t\tResult:    \"resultVar2\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tbindings: map[string]string{\n\t\t\t\t\"var1\": \"value1\",\n\t\t\t\t\"var2\": \"value2\",\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Test Parallel Execution\",\n\t\t\tfields: Statement{\n\t\t\t\tParallel: &Parallel{\n\t\t\t\t\tBranches: []*Statement{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tActivity: &ActivityInvocation{\n\t\t\t\t\t\t\t\tName:      \"sampleActivity\",\n\t\t\t\t\t\t\t\tArguments: []string{\"var1\"},\n\t\t\t\t\t\t\t\tResult:    \"resultVar1\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tActivity: &ActivityInvocation{\n\t\t\t\t\t\t\t\tName:      \"sampleActivity\",\n\t\t\t\t\t\t\t\tArguments: []string{\"var2\"},\n\t\t\t\t\t\t\t\tResult:    \"resultVar2\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tbindings: map[string]string{\n\t\t\t\t\"var1\": \"value1\",\n\t\t\t\t\"var2\": \"value2\",\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tenv := testSuite.NewTestWorkflowEnvironment()\n\t\t\tenv.RegisterActivityWithOptions(sampleActivity, activity.RegisterOptions{\n\t\t\t\tName: \"sampleActivity\",\n\t\t\t})\n\t\t\tenv.ExecuteWorkflow(func(ctx workflow.Context) error {\n\t\t\t\tctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{\n\t\t\t\t\tScheduleToStartTimeout: time.Minute,\n\t\t\t\t\tStartToCloseTimeout:    time.Minute,\n\t\t\t\t})\n\t\t\t\treturn tt.fields.execute(ctx, tt.bindings)\n\t\t\t})\n\n\t\t\trequire.True(t, env.IsWorkflowCompleted())\n\t\t\tif tt.wantErr {\n\t\t\t\trequire.Error(t, env.GetWorkflowError())\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, env.GetWorkflowError())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSequenceFlow(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\n\ttests := []struct {\n\t\tname     string\n\t\tfields   Sequence\n\t\tbindings map[string]string\n\t\twantErr  bool\n\t}{\n\t\t{\n\t\t\tname: \"Test Sequence Execution with Single Activity\",\n\t\t\tfields: Sequence{\n\t\t\t\tElements: []*Statement{\n\t\t\t\t\t{\n\t\t\t\t\t\tActivity: &ActivityInvocation{\n\t\t\t\t\t\t\tName:      \"sampleActivity\",\n\t\t\t\t\t\t\tArguments: []string{\"var1\"},\n\t\t\t\t\t\t\tResult:    \"resultVar1\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tbindings: map[string]string{\n\t\t\t\t\"var1\": \"value1\",\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Test Sequence Execution with Multiple Activities\",\n\t\t\tfields: Sequence{\n\t\t\t\tElements: []*Statement{\n\t\t\t\t\t{\n\t\t\t\t\t\tActivity: &ActivityInvocation{\n\t\t\t\t\t\t\tName:      \"sampleActivity\",\n\t\t\t\t\t\t\tArguments: []string{\"var1\"},\n\t\t\t\t\t\t\tResult:    \"resultVar1\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tActivity: &ActivityInvocation{\n\t\t\t\t\t\t\tName:      \"sampleActivity\",\n\t\t\t\t\t\t\tArguments: []string{\"var2\"},\n\t\t\t\t\t\t\tResult:    \"resultVar2\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tbindings: map[string]string{\n\t\t\t\t\"var1\": \"value1\",\n\t\t\t\t\"var2\": \"value2\",\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Test Sequence Execution with Error\",\n\t\t\tfields: Sequence{\n\t\t\t\tElements: []*Statement{\n\t\t\t\t\t{\n\t\t\t\t\t\tActivity: &ActivityInvocation{\n\t\t\t\t\t\t\tName:      \"sampleActivity\",\n\t\t\t\t\t\t\tArguments: []string{\"var1\"},\n\t\t\t\t\t\t\tResult:    \"resultVar1\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tActivity: &ActivityInvocation{\n\t\t\t\t\t\t\tName:      \"nonExistentActivity\",\n\t\t\t\t\t\t\tArguments: []string{\"var2\"},\n\t\t\t\t\t\t\tResult:    \"resultVar2\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tbindings: map[string]string{\n\t\t\t\t\"var1\": \"value1\",\n\t\t\t\t\"var2\": \"value2\",\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tenv := testSuite.NewTestWorkflowEnvironment()\n\t\t\tenv.RegisterActivityWithOptions(sampleActivity, activity.RegisterOptions{\n\t\t\t\tName: \"sampleActivity\",\n\t\t\t})\n\n\t\t\tenv.ExecuteWorkflow(func(ctx workflow.Context) error {\n\t\t\t\tctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{\n\t\t\t\t\tScheduleToStartTimeout: time.Minute,\n\t\t\t\t\tStartToCloseTimeout:    time.Minute,\n\t\t\t\t})\n\t\t\t\treturn tt.fields.execute(ctx, tt.bindings)\n\t\t\t})\n\n\t\t\trequire.True(t, env.IsWorkflowCompleted())\n\t\t\tif tt.wantErr {\n\t\t\t\trequire.Error(t, env.GetWorkflowError())\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, env.GetWorkflowError())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParallelFlow(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\n\ttests := []struct {\n\t\tname     string\n\t\tfields   Parallel\n\t\tbindings map[string]string\n\t\twantErr  bool\n\t}{\n\t\t{\n\t\t\tname: \"Test Parallel Execution with Single Activity\",\n\t\t\tfields: Parallel{\n\t\t\t\tBranches: []*Statement{\n\t\t\t\t\t{\n\t\t\t\t\t\tActivity: &ActivityInvocation{\n\t\t\t\t\t\t\tName:      \"sampleActivity\",\n\t\t\t\t\t\t\tArguments: []string{\"var1\"},\n\t\t\t\t\t\t\tResult:    \"resultVar1\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tbindings: map[string]string{\n\t\t\t\t\"var1\": \"value1\",\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Test Parallel Execution with Multiple Activities\",\n\t\t\tfields: Parallel{\n\t\t\t\tBranches: []*Statement{\n\t\t\t\t\t{\n\t\t\t\t\t\tActivity: &ActivityInvocation{\n\t\t\t\t\t\t\tName:      \"sampleActivity\",\n\t\t\t\t\t\t\tArguments: []string{\"var1\"},\n\t\t\t\t\t\t\tResult:    \"resultVar1\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tActivity: &ActivityInvocation{\n\t\t\t\t\t\t\tName:      \"sampleActivity\",\n\t\t\t\t\t\t\tArguments: []string{\"var2\"},\n\t\t\t\t\t\t\tResult:    \"resultVar2\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tbindings: map[string]string{\n\t\t\t\t\"var1\": \"value1\",\n\t\t\t\t\"var2\": \"value2\",\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Test Parallel Execution with Error\",\n\t\t\tfields: Parallel{\n\t\t\t\tBranches: []*Statement{\n\t\t\t\t\t{\n\t\t\t\t\t\tActivity: &ActivityInvocation{\n\t\t\t\t\t\t\tName:      \"sampleActivity\",\n\t\t\t\t\t\t\tArguments: []string{\"var1\"},\n\t\t\t\t\t\t\tResult:    \"resultVar1\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tActivity: &ActivityInvocation{\n\t\t\t\t\t\t\tName:      \"nonExistentActivity\",\n\t\t\t\t\t\t\tArguments: []string{\"var2\"},\n\t\t\t\t\t\t\tResult:    \"resultVar2\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tbindings: map[string]string{\n\t\t\t\t\"var1\": \"value1\",\n\t\t\t\t\"var2\": \"value2\",\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tenv := testSuite.NewTestWorkflowEnvironment()\n\t\t\tenv.RegisterActivityWithOptions(sampleActivity, activity.RegisterOptions{\n\t\t\t\tName: \"sampleActivity\",\n\t\t\t})\n\n\t\t\tenv.ExecuteWorkflow(func(ctx workflow.Context) error {\n\t\t\t\tctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{\n\t\t\t\t\tScheduleToStartTimeout: time.Minute,\n\t\t\t\t\tStartToCloseTimeout:    time.Minute,\n\t\t\t\t})\n\t\t\t\treturn tt.fields.execute(ctx, tt.bindings)\n\t\t\t})\n\n\t\t\trequire.True(t, env.IsWorkflowCompleted())\n\t\t\tif tt.wantErr {\n\t\t\t\trequire.Error(t, env.GetWorkflowError())\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, env.GetWorkflowError())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestActivityInvocationFlow(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\n\ttests := []struct {\n\t\tname     string\n\t\tfields   ActivityInvocation\n\t\tbindings map[string]string\n\t\twantErr  bool\n\t}{\n\t\t{\n\t\t\tname: \"Test Activity Invocation Success\",\n\t\t\tfields: ActivityInvocation{\n\t\t\t\tName:      \"sampleActivity\",\n\t\t\t\tArguments: []string{\"var1\"},\n\t\t\t\tResult:    \"resultVar\",\n\t\t\t},\n\t\t\tbindings: map[string]string{\n\t\t\t\t\"var1\": \"value1\",\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Test Activity Invocation with Error\",\n\t\t\tfields: ActivityInvocation{\n\t\t\t\tName:      \"nonExistentActivity\",\n\t\t\t\tArguments: []string{\"var1\"},\n\t\t\t\tResult:    \"resultVar\",\n\t\t\t},\n\t\t\tbindings: map[string]string{\n\t\t\t\t\"var1\": \"value1\",\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tenv := testSuite.NewTestWorkflowEnvironment()\n\t\t\tenv.RegisterActivityWithOptions(sampleActivity, activity.RegisterOptions{\n\t\t\t\tName: \"sampleActivity\",\n\t\t\t})\n\n\t\t\tenv.ExecuteWorkflow(func(ctx workflow.Context) error {\n\t\t\t\tctx = workflow.WithActivityOptions(ctx, workflow.ActivityOptions{\n\t\t\t\t\tScheduleToStartTimeout: time.Minute,\n\t\t\t\t\tStartToCloseTimeout:    time.Minute,\n\t\t\t\t})\n\t\t\t\treturn tt.fields.execute(ctx, tt.bindings)\n\t\t\t})\n\n\t\t\trequire.True(t, env.IsWorkflowCompleted())\n\t\t\tif tt.wantErr {\n\t\t\t\trequire.Error(t, env.GetWorkflowError())\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, env.GetWorkflowError())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_SimpleDSLWorkflow(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\tenv := testSuite.NewTestWorkflowEnvironment()\n\n\t// Define a sample DSL workflow\n\tdslWorkflow := Workflow{\n\t\tVariables: map[string]string{\n\t\t\t\"var1\": \"value1\",\n\t\t\t\"var2\": \"value2\",\n\t\t},\n\t\tRoot: Statement{\n\t\t\tActivity: &ActivityInvocation{\n\t\t\t\tName:      \"sampleActivity\",\n\t\t\t\tArguments: []string{\"var1\", \"var2\"},\n\t\t\t\tResult:    \"resultVar\",\n\t\t\t},\n\t\t},\n\t}\n\n\t// Register a sample activity\n\tenv.RegisterActivityWithOptions(sampleActivity, activity.RegisterOptions{\n\t\tName: \"sampleActivity\",\n\t})\n\n\tenv.ExecuteWorkflow(simpleDSLWorkflow, dslWorkflow)\n\n\trequire.True(t, env.IsWorkflowCompleted())\n\trequire.NoError(t, env.GetWorkflowError())\n}\n\nfunc sampleActivity(input []string) (string, error) {\n\tname := \"sampleActivity\"\n\tfmt.Printf(\"Run %s with input %v \\n\", name, input)\n\treturn \"Result_\" + name, nil\n}\n"
  },
  {
    "path": "cmd/samples/expense/README.md",
    "content": "# Expense\nThis sample workflow process an expense request. The key part of this sample is to show how to complete an activity asynchronously.\n\n# Sample Description\n* Create a new expense report.\n* Wait for the expense report to be approved. This could take an arbitrary amount of time. So the activity's Execute method has to return before it is actually approved. This is done by returning a special error so the framework knows the activity is not completed yet.\n  * When the expense is approved (or rejected), somewhere in the world needs to be notified, and it will need to call WorkflowClient.CompleteActivity() to tell cadence service that that activity is now completed. In this sample case, the dummy server do this job. In real world, you will need to register some listener to the expense system or you will need to have your own pulling agent to check for the expense status periodic.\n* After the wait activity is completed, it did the payment for the expense. (dummy step in this sample case)\n\nThis sample rely on an a dummy expense server to work.\n\n# Steps To Run Sample\n* You need a cadence service running. See https://github.com/cadence-workflow/cadence/blob/master/README.md for more details.\n* Start the dummy server\n```\n./bin/expense_dummy\n```\nIf dummy is not found, run make to build it.\n* Start workflow and activity workers\n```\n./bin/expense -m worker\n```\n* Start expanse workflow execution\n```\n./bin/expense -m trigger\n```\n* When you see the console print out the expense is created, go to [localhost:8099/list](http://localhost:8099/list) to approve the expense.\n* You should see the workflow complete after you approve the expense. You can also reject the expense.\n* If you see the workflow failed, try to change to a different port number in dummy.go and workflow.go. Then rebuild everything.\n"
  },
  {
    "path": "cmd/samples/expense/activities.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/zap\"\n)\n\nfunc createExpenseActivity(ctx context.Context, expenseID string) error {\n\tif len(expenseID) == 0 {\n\t\treturn errors.New(\"expense id is empty\")\n\t}\n\n\tresp, err := http.Get(expenseServerHostPort + \"/create?is_api_call=true&id=\" + expenseID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbody, err := ioutil.ReadAll(resp.Body)\n\tresp.Body.Close()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif string(body) == \"SUCCEED\" {\n\t\tactivity.GetLogger(ctx).Info(\"Expense created.\", zap.String(\"ExpenseID\", expenseID))\n\t\treturn nil\n\t}\n\n\treturn errors.New(string(body))\n}\n\n// waitForDecisionActivity waits for the expense decision. This activity will complete asynchronously. When this method\n// returns error activity.ErrResultPending, the cadence client recognize this error, and won't mark this activity\n// as failed or completed. The cadence server will wait until Client.CompleteActivity() is called or timeout happened\n// whichever happen first. In this sample case, the CompleteActivity() method is called by our dummy expense server when\n// the expense is approved.\nfunc waitForDecisionActivity(ctx context.Context, expenseID string) (string, error) {\n\tif len(expenseID) == 0 {\n\t\treturn \"\", errors.New(\"expense id is empty\")\n\t}\n\n\tlogger := activity.GetLogger(ctx)\n\n\t// save current activity info so it can be completed asynchronously when expense is approved/rejected\n\tactivityInfo := activity.GetInfo(ctx)\n\tformData := url.Values{}\n\tformData.Add(\"task_token\", string(activityInfo.TaskToken))\n\n\tregisterCallbackURL := expenseServerHostPort + \"/registerCallback?id=\" + expenseID\n\tresp, err := http.PostForm(registerCallbackURL, formData)\n\tif err != nil {\n\t\tlogger.Info(\"waitForDecisionActivity failed to register callback.\", zap.Error(err))\n\t\treturn \"\", err\n\t}\n\tbody, err := ioutil.ReadAll(resp.Body)\n\tresp.Body.Close()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tstatus := string(body)\n\tif status == \"SUCCEED\" {\n\t\t// register callback succeed\n\t\tlogger.Info(\"Successfully registered callback.\", zap.String(\"ExpenseID\", expenseID))\n\n\t\t// ErrActivityResultPending is returned from activity's execution to indicate the activity is not completed when it returns.\n\t\t// activity will be completed asynchronously when Client.CompleteActivity() is called.\n\t\treturn \"\", activity.ErrResultPending\n\t}\n\n\tlogger.Warn(\"Register callback failed.\", zap.String(\"ExpenseStatus\", status))\n\treturn \"\", fmt.Errorf(\"register callback failed status:%s\", status)\n}\n\nfunc paymentActivity(ctx context.Context, expenseID string) error {\n\tif len(expenseID) == 0 {\n\t\treturn errors.New(\"expense id is empty\")\n\t}\n\n\tresp, err := http.Get(expenseServerHostPort + \"/action?is_api_call=true&type=payment&id=\" + expenseID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbody, err := ioutil.ReadAll(resp.Body)\n\tresp.Body.Close()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif string(body) == \"SUCCEED\" {\n\t\tactivity.GetLogger(ctx).Info(\"paymentActivity succeed\", zap.String(\"ExpenseID\", expenseID))\n\t\treturn nil\n\t}\n\n\treturn errors.New(string(body))\n}\n"
  },
  {
    "path": "cmd/samples/expense/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper, expenseID string) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"expense_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute * 12,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute * 12,\n\t}\n\th.StartWorkflow(workflowOptions, sampleExpenseWorkflow, expenseID)\n}\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(sampleExpenseWorkflow)\n\t\th.RegisterActivity(createExpenseActivity)\n\t\th.RegisterActivity(waitForDecisionActivity)\n\t\th.RegisterActivity(paymentActivity)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h, uuid.New())\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/expense/server/dummy.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"sort\"\n\n\t\"go.uber.org/cadence/client\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n/**\n * Dummy server that support to list expenses, create new expense, update expense state and checking expense state.\n */\n\ntype expenseState string\n\nconst (\n\tcreated   expenseState = \"CREATED\"\n\tapproved               = \"APPROVED\"\n\trejected               = \"REJECTED\"\n\tcompleted              = \"COMPLETED\"\n)\n\n// use memory store for this dummy server\nvar allExpense = make(map[string]expenseState)\n\nvar tokenMap = make(map[string][]byte)\n\nvar workflowClient client.Client\n\nfunc main() {\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\tvar err error\n\tworkflowClient, err = h.Builder.BuildCadenceClient()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(\"Starting dummy server...\")\n\thttp.HandleFunc(\"/\", listHandler)\n\thttp.HandleFunc(\"/list\", listHandler)\n\thttp.HandleFunc(\"/create\", createHandler)\n\thttp.HandleFunc(\"/action\", actionHandler)\n\thttp.HandleFunc(\"/status\", statusHandler)\n\thttp.HandleFunc(\"/registerCallback\", callbackHandler)\n\thttp.ListenAndServe(\":8099\", nil)\n}\n\nfunc listHandler(w http.ResponseWriter, r *http.Request) {\n\tfmt.Fprint(w, \"<h1>DUMMY EXPENSE SYSTEM</h1>\"+\"<a href=\\\"/list\\\">HOME</a>\"+\n\t\t\"<h3>All expense requests:</h3><table border=1><tr><th>Expense ID</th><th>Status</th><th>Action</th>\")\n\tkeys := []string{}\n\tfor k := range allExpense {\n\t\tkeys = append(keys, k)\n\t}\n\tsort.Strings(keys)\n\tfor _, id := range keys {\n\t\tstate := allExpense[id]\n\t\tactionLink := \"\"\n\t\tif state == created {\n\t\t\tactionLink = fmt.Sprintf(\"<a href=\\\"/action?type=approve&id=%s\\\">\"+\n\t\t\t\t\"<button style=\\\"background-color:#4CAF50;\\\">APPROVE</button></a>\"+\n\t\t\t\t\"&nbsp;&nbsp;<a href=\\\"/action?type=reject&id=%s\\\">\"+\n\t\t\t\t\"<button style=\\\"background-color:#f44336;\\\">REJECT</button></a>\", id, id)\n\t\t}\n\t\tfmt.Fprintf(w, \"<tr><td>%s</td><td>%s</td><td>%s</td></tr>\", id, state, actionLink)\n\t}\n\tfmt.Fprint(w, \"</table>\")\n}\n\nfunc actionHandler(w http.ResponseWriter, r *http.Request) {\n\tisAPICall := r.URL.Query().Get(\"is_api_call\") == \"true\"\n\tid := r.URL.Query().Get(\"id\")\n\toldState, ok := allExpense[id]\n\tif !ok {\n\t\tfmt.Fprint(w, \"ERROR:INVALID_ID\")\n\t\treturn\n\t}\n\tactionType := r.URL.Query().Get(\"type\")\n\tswitch actionType {\n\tcase \"approve\":\n\t\tallExpense[id] = approved\n\tcase \"reject\":\n\t\tallExpense[id] = rejected\n\tcase \"payment\":\n\t\tallExpense[id] = completed\n\t}\n\tif isAPICall {\n\t\tfmt.Fprint(w, \"SUCCEED\")\n\t} else {\n\t\tlistHandler(w, r)\n\t}\n\n\tif oldState == created && (allExpense[id] == approved || allExpense[id] == rejected) {\n\t\t// report state change\n\t\tnotifyExpenseStateChange(id, string(allExpense[id]))\n\t}\n\n\tfmt.Printf(\"Set state for %s from %s to %s.\\n\", id, oldState, allExpense[id])\n\treturn\n}\n\nfunc createHandler(w http.ResponseWriter, r *http.Request) {\n\tisAPICall := r.URL.Query().Get(\"is_api_call\") == \"true\"\n\tid := r.URL.Query().Get(\"id\")\n\t_, ok := allExpense[id]\n\tif ok {\n\t\tfmt.Fprint(w, \"ERROR:ID_ALREADY_EXISTS\")\n\t\treturn\n\t}\n\n\tallExpense[id] = created\n\tif isAPICall {\n\t\tfmt.Fprint(w, \"SUCCEED\")\n\t} else {\n\t\tlistHandler(w, r)\n\t}\n\tfmt.Printf(\"Created new expense id:%s.\\n\", id)\n\treturn\n}\n\nfunc statusHandler(w http.ResponseWriter, r *http.Request) {\n\tid := r.URL.Query().Get(\"id\")\n\tstate, ok := allExpense[id]\n\tif !ok {\n\t\tfmt.Fprint(w, \"ERROR:INVALID_ID\")\n\t\treturn\n\t}\n\n\tfmt.Fprint(w, state)\n\tfmt.Printf(\"Checking status for %s: %s\\n\", id, state)\n\treturn\n}\n\nfunc callbackHandler(w http.ResponseWriter, r *http.Request) {\n\tid := r.URL.Query().Get(\"id\")\n\tcurrState, ok := allExpense[id]\n\tif !ok {\n\t\tfmt.Fprint(w, \"ERROR:INVALID_ID\")\n\t\treturn\n\t}\n\tif currState != created {\n\t\tfmt.Fprint(w, \"ERROR:INVALID_STATE\")\n\t\treturn\n\t}\n\n\terr := r.ParseForm()\n\tif err != nil {\n\t\t// Handle error here via logging and then return\n\t\tfmt.Fprint(w, \"ERROR:INVALID_FORM_DATA\")\n\t\treturn\n\t}\n\n\ttaskToken := r.PostFormValue(\"task_token\")\n\tfmt.Printf(\"Registered callback for ID=%s, token=%s\\n\", id, taskToken)\n\ttokenMap[id] = []byte(taskToken)\n\tfmt.Fprint(w, \"SUCCEED\")\n}\n\nfunc notifyExpenseStateChange(id, state string) {\n\ttoken, ok := tokenMap[id]\n\tif !ok {\n\t\tfmt.Printf(\"Invalid id:%s\\n\", id)\n\t\treturn\n\t}\n\terr := workflowClient.CompleteActivity(context.Background(), token, state, nil)\n\tif err != nil {\n\t\tfmt.Printf(\"Failed to complete activity with error: %+v\\n\", err)\n\t} else {\n\t\tfmt.Printf(\"Successfully complete activity: %s\\n\", token)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/expense/workflow.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\nconst (\n\t// ApplicationName is the task list for this sample\n\tApplicationName = \"expenseGroup\"\n)\n\nvar expenseServerHostPort = \"http://localhost:8099\"\n\n// sampleExpenseWorkflow workflow decider\nfunc sampleExpenseWorkflow(ctx workflow.Context, expenseID string) (result string, err error) {\n\t// step 1, create new expense report\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx1 := workflow.WithActivityOptions(ctx, ao)\n\tlogger := workflow.GetLogger(ctx)\n\n\terr = workflow.ExecuteActivity(ctx1, createExpenseActivity, expenseID).Get(ctx1, nil)\n\tif err != nil {\n\t\tlogger.Error(\"Failed to create expense report\", zap.Error(err))\n\t\treturn \"\", err\n\t}\n\n\t// step 2, wait for the expense report to be approved (or rejected)\n\tao = workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    4 * time.Minute,\n\t}\n\tctx2 := workflow.WithActivityOptions(ctx, ao)\n\t// Notice that we set the timeout to be 4 minutes for this sample demo. If the expected time for the activity to\n\t// complete (waiting for human to approve the request) is longer, you should set the timeout accordingly so the\n\t// cadence system will wait accordingly. Otherwise, cadence system could mark the activity as failure by timeout.\n\tvar status string\n\terr = workflow.ExecuteActivity(ctx2, waitForDecisionActivity, expenseID).Get(ctx2, &status)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif status != \"APPROVED\" {\n\t\tlogger.Info(\"Workflow completed.\", zap.String(\"ExpenseStatus\", status))\n\t\treturn \"\", nil\n\t}\n\n\t// step 3, request payment to the expense\n\terr = workflow.ExecuteActivity(ctx2, paymentActivity, expenseID).Get(ctx2, nil)\n\tif err != nil {\n\t\tlogger.Info(\"Workflow completed with payment failed.\", zap.Error(err))\n\t\treturn \"\", err\n\t}\n\n\tlogger.Info(\"Workflow completed with expense payment completed.\")\n\treturn \"COMPLETED\", nil\n}\n"
  },
  {
    "path": "cmd/samples/expense/workflow_test.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/mock\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\ntype UnitTestSuite struct {\n\tsuite.Suite\n\ttestsuite.WorkflowTestSuite\n\n\tenv *testsuite.TestWorkflowEnvironment\n}\n\nfunc TestUnitTestSuite(t *testing.T) {\n\tsuite.Run(t, new(UnitTestSuite))\n}\n\nfunc (s *UnitTestSuite) SetupTest() {\n\ts.env = s.NewTestWorkflowEnvironment()\n\ts.env.RegisterWorkflow(sampleExpenseWorkflow)\n\ts.env.RegisterActivity(createExpenseActivity)\n\ts.env.RegisterActivity(waitForDecisionActivity)\n\ts.env.RegisterActivity(paymentActivity)\n}\n\nfunc (s *UnitTestSuite) TearDownTest() {\n\ts.env.AssertExpectations(s.T())\n}\n\nfunc (s *UnitTestSuite) Test_WorkflowWithMockActivities() {\n\ts.env.OnActivity(createExpenseActivity, mock.Anything, mock.Anything).Return(nil).Once()\n\ts.env.OnActivity(waitForDecisionActivity, mock.Anything, mock.Anything).Return(\"APPROVED\", nil).Once()\n\ts.env.OnActivity(paymentActivity, mock.Anything, mock.Anything).Return(nil).Once()\n\n\ts.env.ExecuteWorkflow(sampleExpenseWorkflow, \"test-expense-id\")\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n\tvar workflowResult string\n\terr := s.env.GetWorkflowResult(&workflowResult)\n\ts.NoError(err)\n\ts.Equal(\"COMPLETED\", workflowResult)\n}\n\nfunc (s *UnitTestSuite) Test_TimeoutWithMockActivities() {\n\ts.env.OnActivity(createExpenseActivity, mock.Anything, mock.Anything).Return(nil).Once()\n\ts.env.SetWorkflowTimeout(time.Microsecond * 500)\n\ts.env.SetTestTimeout(time.Minute * 10)\n\n\ts.env.ExecuteWorkflow(sampleExpenseWorkflow, \"test-expense-id\")\n\n\tvar workflowResult string\n\terr := s.env.GetWorkflowResult(&workflowResult)\n\ts.Equal(\"TimeoutType: SCHEDULE_TO_CLOSE\", err.Error())\n\ts.Empty(workflowResult)\n}\n\nfunc (s *UnitTestSuite) Test_WorkflowStatusRejected() {\n\ts.env.OnActivity(createExpenseActivity, mock.Anything, mock.Anything).Return(nil).Once()\n\ts.env.OnActivity(waitForDecisionActivity, mock.Anything, mock.Anything).Return(\"REJECTED\", nil).Once()\n\n\ts.env.ExecuteWorkflow(sampleExpenseWorkflow, \"test-expense-id\")\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n\tvar workflowResult string\n\terr := s.env.GetWorkflowResult(&workflowResult)\n\ts.NoError(err)\n\ts.Empty(workflowResult)\n}\n\nfunc (s *UnitTestSuite) Test_WorkflowStatusCancelled() {\n\ts.env.OnActivity(createExpenseActivity, mock.Anything, mock.Anything).Return(nil).Once()\n\ts.env.OnActivity(waitForDecisionActivity, mock.Anything, mock.Anything).Return(\"CANCELLED\", nil).Once()\n\n\ts.env.ExecuteWorkflow(sampleExpenseWorkflow, \"test-expense-id\")\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n\tvar workflowResult string\n\terr := s.env.GetWorkflowResult(&workflowResult)\n\ts.NoError(err)\n\ts.Empty(workflowResult)\n}\n\nfunc (s *UnitTestSuite) Test_WorkflowStatusApprovedWithPaymentError() {\n\ts.env.OnActivity(createExpenseActivity, mock.Anything, mock.Anything).Return(nil).Once()\n\ts.env.OnActivity(waitForDecisionActivity, mock.Anything, mock.Anything).Return(\"APPROVED\", nil).Once()\n\ts.env.OnActivity(paymentActivity, mock.Anything, mock.Anything).Return(errors.New(\"payment error\")).Once()\n\n\ts.env.ExecuteWorkflow(sampleExpenseWorkflow, \"test-expense-id\")\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.Error(s.env.GetWorkflowError())\n\tvar workflowResult string\n\terr := s.env.GetWorkflowResult(&workflowResult)\n\ts.Equal(\"payment error\", err.Error())\n\ts.Empty(workflowResult)\n}\n\nfunc (s *UnitTestSuite) Test_CreateActivityFailed() {\n\ts.env.OnActivity(createExpenseActivity, mock.Anything, mock.Anything).Return(errors.New(\"expense id is empty\")).Once()\n\n\ts.env.ExecuteWorkflow(sampleExpenseWorkflow, \"\")\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.Error(s.env.GetWorkflowError())\n\tvar workflowResult string\n\n\terr := s.env.GetWorkflowResult(&workflowResult)\n\ts.Equal(\"expense id is empty\", err.Error())\n\ts.Empty(workflowResult)\n}\n\nfunc (s *UnitTestSuite) Test_WaitForDecisionActivityFailed() {\n\ts.env.OnActivity(createExpenseActivity, mock.Anything, mock.Anything).Return(nil).Once()\n\ts.env.OnActivity(waitForDecisionActivity, mock.Anything, mock.Anything).Return(\"\", errors.New(\"failed to get decision\")).Once()\n\n\ts.env.ExecuteWorkflow(sampleExpenseWorkflow, \"test-expense-id\")\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.Error(s.env.GetWorkflowError())\n\tvar workflowResult string\n\n\terr := s.env.GetWorkflowResult(&workflowResult)\n\ts.Equal(\"failed to get decision\", err.Error())\n\ts.Empty(workflowResult)\n}\n\nfunc (s *UnitTestSuite) Test_PaymentActivityFailed() {\n\ts.env.OnActivity(createExpenseActivity, mock.Anything, mock.Anything).Return(nil).Once()\n\ts.env.OnActivity(waitForDecisionActivity, mock.Anything, mock.Anything).Return(\"APPROVED\", nil).Once()\n\ts.env.OnActivity(paymentActivity, mock.Anything, mock.Anything).Return(errors.New(\"payment failed\")).Once()\n\n\ts.env.ExecuteWorkflow(sampleExpenseWorkflow, \"test-expense-id\")\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.Error(s.env.GetWorkflowError())\n\tvar workflowResult string\n\n\terr := s.env.GetWorkflowResult(&workflowResult)\n\ts.Equal(\"payment failed\", err.Error())\n\ts.Empty(workflowResult)\n}\n\nfunc (s *UnitTestSuite) Test_WorkflowWithMockServer() {\n\t// setup mock expense server\n\thandler := func(w http.ResponseWriter, r *http.Request) {\n\t\tw.Header().Set(\"Content-Type\", \"application/text\")\n\t\tswitch r.URL.Path {\n\t\tcase \"/create\":\n\t\tcase \"/registerCallback\":\n\t\t\ttaskToken := []byte(r.PostFormValue(\"task_token\"))\n\t\t\t// simulate the expense is approved one hour later.\n\t\t\ts.env.RegisterDelayedCallback(func() {\n\t\t\t\ts.env.CompleteActivity(taskToken, \"APPROVED\", nil)\n\t\t\t}, time.Hour)\n\t\tcase \"/action\":\n\t\t}\n\t\tio.WriteString(w, \"SUCCEED\")\n\t}\n\tserver := httptest.NewServer(http.HandlerFunc(handler))\n\tdefer server.Close()\n\n\t// pointing server to test mock\n\texpenseServerHostPort = server.URL\n\n\ts.env.ExecuteWorkflow(sampleExpenseWorkflow, \"test-expense-id\")\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n\tvar workflowResult string\n\terr := s.env.GetWorkflowResult(&workflowResult)\n\ts.NoError(err)\n\ts.Equal(\"COMPLETED\", workflowResult)\n}\n"
  },
  {
    "path": "cmd/samples/fileprocessing/README.md",
    "content": "This sample workflow demos a file processing process. The key part is to show how to use the session API.\n\nThe workflow first starts an activity to download a requested resource file from web and store it locally on the host where it runs the download activity. Then, the workflow will start more activities to process the downloaded resource file. The key part is the following activities have to be run on the same host as the initial downloading activity. This is achieved by using the session API.\n\nSteps for using Session API:\n1) When starting worker, set `EnableSessionWorker` to true in workerOptions.\n2) In the workflow code, create a new session using the `CreateSession()` API\n```\n  so := &workflow.SessionOptions{\n    CreationTimeout:  time.Minute,\n    ExecutionTimeout: time.Minute,\n  }\n  sessionCtx, err := workflow.CreateSession(ctx, so)\n```\n3) Use the returned `sessionCtx` or its child context to execute activities. These activities will be to scheduled on the same host.\n4) After all activites are executed, call `CompleteSession()`.\n```\n  workflow.CompleteSession(sessionCtx)\n```\n5) Check the inline document in workflow/session.go of the go-client repo for more advanced usage.\n\nSteps to run this sample:\n1) You need a cadence service running. See details in cmd/samples/README.md\n2) Run the following command multiple times on different console window. This is to simulate running workers on multiple different machines.\n```\n./bin/fileprocessing -m worker\n```\n3) Run the following command to submit a start request for this fileprocessing workflow.\n```\n./bin/fileprocessing -m trigger\n```\n\nYou should see that all activities for one particular workflow execution are scheduled to run on one console window.\n"
  },
  {
    "path": "cmd/samples/fileprocessing/activities.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * Sample activities used by file processing sample workflow.\n */\nconst (\n\tdownloadFileActivityName = \"downloadFileActivity\"\n\tprocessFileActivityName  = \"processFileActivity\"\n\tuploadFileActivityName   = \"uploadFileActivity\"\n)\n\nfunc downloadFileActivity(ctx context.Context, fileID string) (*fileInfo, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"Downloading file...\", zap.String(\"FileID\", fileID))\n\tdata := downloadFile(fileID)\n\n\ttmpFile, err := saveToTmpFile(data)\n\tif err != nil {\n\t\tlogger.Error(\"downloadFileActivity failed to save tmp file.\", zap.Error(err))\n\t\treturn nil, err\n\t}\n\n\tfileInfo := &fileInfo{FileName: tmpFile.Name(), HostID: HostID}\n\tlogger.Info(\"downloadFileActivity succeed.\", zap.String(\"SavedFilePath\", fileInfo.FileName))\n\treturn fileInfo, nil\n}\n\nfunc processFileActivity(ctx context.Context, fInfo fileInfo) (*fileInfo, error) {\n\tlogger := activity.GetLogger(ctx).With(zap.String(\"HostID\", HostID))\n\tlogger.Info(\"processFileActivity started.\", zap.String(\"FileName\", fInfo.FileName))\n\t// assert that we are running on the same host as the file was downloaded\n\t// this check is not necessary, just to demo the host specific tasklist is working\n\tif fInfo.HostID != HostID {\n\t\tlogger.Error(\"processFileActivity on wrong host\",\n\t\t\tzap.String(\"TargetFile\", fInfo.FileName),\n\t\t\tzap.String(\"TargetHostID\", fInfo.HostID))\n\t\treturn nil, errors.New(\"processFileActivity running on wrong host\")\n\t}\n\n\tdefer os.Remove(fInfo.FileName) // cleanup temp file\n\n\t// read downloaded file\n\tdata, err := ioutil.ReadFile(fInfo.FileName)\n\tif err != nil {\n\t\tlogger.Error(\"processFileActivity failed to read file.\", zap.String(\"FileName\", fInfo.FileName), zap.Error(err))\n\t\treturn nil, err\n\t}\n\n\t// process the file\n\ttransData := transcodeData(ctx, data)\n\ttmpFile, err := saveToTmpFile(transData)\n\tif err != nil {\n\t\tlogger.Error(\"processFileActivity failed to save tmp file.\", zap.Error(err))\n\t\treturn nil, err\n\t}\n\n\tprocessedInfo := &fileInfo{FileName: tmpFile.Name(), HostID: HostID}\n\tlogger.Info(\"processFileActivity succeed.\", zap.String(\"SavedFilePath\", processedInfo.FileName))\n\treturn processedInfo, nil\n}\n\nfunc uploadFileActivity(ctx context.Context, fInfo fileInfo) error {\n\tlogger := activity.GetLogger(ctx).With(zap.String(\"HostID\", HostID))\n\tlogger.Info(\"uploadFileActivity begin.\", zap.String(\"UploadedFileName\", fInfo.FileName))\n\n\t// assert that we are running on the same host as the file was downloaded\n\t// this check is not necessary, just to demo the host specific tasklist is working\n\tif fInfo.HostID != HostID {\n\t\tlogger.Error(\"uploadFileActivity on wrong host\",\n\t\t\tzap.String(\"TargetFile\", fInfo.FileName),\n\t\t\tzap.String(\"TargetHostID\", fInfo.HostID))\n\t\treturn errors.New(\"uploadFileActivity running on wrong host\")\n\t}\n\n\tdefer os.Remove(fInfo.FileName) // clean up tmp file\n\n\terr := uploadFile(ctx, fInfo.FileName)\n\tif err != nil {\n\t\tlogger.Error(\"uploadFileActivity uploading failed.\", zap.Error(err))\n\t\treturn err\n\t}\n\tlogger.Info(\"uploadFileActivity succeed.\", zap.String(\"UploadedFileName\", fInfo.FileName))\n\treturn nil\n}\n\nfunc downloadFile(fileID string) []byte {\n\t// dummy downloader\n\tdummyContent := \"dummy content for fileID:\" + fileID\n\treturn []byte(dummyContent)\n}\n\nfunc uploadFile(ctx context.Context, filename string) error {\n\t// dummy uploader\n\t_, err := ioutil.ReadFile(filename)\n\tfor i := 0; i < 5; i++ {\n\t\ttime.Sleep(1 * time.Second)\n\t\t// Demonstrates that heartbeat accepts progress data.\n\t\t// In case of a heartbeat timeout it is included into the error.\n\t\tactivity.RecordHeartbeat(ctx, i)\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc transcodeData(ctx context.Context, data []byte) []byte {\n\t// dummy file processor, just do upper case for the data.\n\t// in real world case, you would want to avoid load entire file content into memory at once.\n\tfor i := 0; i < 5; i++ {\n\t\ttime.Sleep(1 * time.Second)\n\t\t// Demonstrates that heartbeat accepts progress data.\n\t\t// In case of a heartbeat timeout it is included into the error.\n\t\tactivity.RecordHeartbeat(ctx, i)\n\t}\n\treturn []byte(strings.ToUpper(string(data)))\n}\n\nfunc saveToTmpFile(data []byte) (f *os.File, err error) {\n\ttmpFile, err := ioutil.TempFile(\"\", \"cadence_sample\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t_, err = tmpFile.Write(data)\n\tif err != nil {\n\t\tos.Remove(tmpFile.Name())\n\t\treturn nil, err\n\t}\n\n\treturn tmpFile, nil\n}\n"
  },
  {
    "path": "cmd/samples/fileprocessing/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope:          h.WorkerMetricScope,\n\t\tLogger:                h.Logger,\n\t\tEnableLoggingInReplay: true,\n\t\tEnableSessionWorker:   true,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n\n\t// Host Specific activities processing case\n\tworkerOptions.DisableWorkflowWorker = true\n\th.StartWorkers(h.Config.DomainName, HostID, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper, fileID string) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"fileprocessing_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, sampleFileProcessingWorkflow, fileID)\n}\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(sampleFileProcessingWorkflow)\n\t\th.RegisterActivityWithAlias(downloadFileActivity, downloadFileActivityName)\n\t\th.RegisterActivityWithAlias(processFileActivity, processFileActivityName)\n\t\th.RegisterActivityWithAlias(uploadFileActivity, uploadFileActivityName)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h, uuid.New())\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/fileprocessing/workflow.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\ntype (\n\tfileInfo struct {\n\t\tFileName string\n\t\tHostID   string\n\t}\n)\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"FileProcessorGroup\"\n\n// HostID - Use a new uuid just for demo so we can run 2 host specific activity workers on same machine.\n// In real world case, you would use a hostname or ip address as HostID.\nvar HostID = ApplicationName + \"_\" + uuid.New()\n\n//sampleFileProcessingWorkflow workflow decider\nfunc sampleFileProcessingWorkflow(ctx workflow.Context, fileID string) (err error) {\n\t// step 1: download resource file\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Second * 5,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 2, // such a short timeout to make sample fail over very fast\n\t\tRetryPolicy: &cadence.RetryPolicy{\n\t\t\tInitialInterval:          time.Second,\n\t\t\tBackoffCoefficient:       2.0,\n\t\t\tMaximumInterval:          time.Minute,\n\t\t\tExpirationInterval:       time.Minute * 10,\n\t\t\tNonRetriableErrorReasons: []string{\"bad-error\"},\n\t\t},\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\t// Retry the whole sequence from the first activity on any error\n\t// to retry it on a different host. In a real application it might be reasonable to\n\t// retry individual activities and the whole sequence discriminating between different types of errors.\n\t// See the retryactivity sample for a more sophisticated retry implementation.\n\tfor i := 1; i < 5; i++ {\n\t\terr = processFile(ctx, fileID)\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\t}\n\tif err != nil {\n\t\tworkflow.GetLogger(ctx).Error(\"Workflow failed.\", zap.String(\"Error\", err.Error()))\n\t} else {\n\t\tworkflow.GetLogger(ctx).Info(\"Workflow completed.\")\n\t}\n\treturn err\n}\n\nfunc processFile(ctx workflow.Context, fileID string) (err error) {\n\tvar fInfo *fileInfo\n\tso := &workflow.SessionOptions{\n\t\tCreationTimeout:  time.Minute,\n\t\tExecutionTimeout: time.Minute,\n\t}\n\n\tsessionCtx, err := workflow.CreateSession(ctx, so)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer workflow.CompleteSession(sessionCtx)\n\n\terr = workflow.ExecuteActivity(sessionCtx, downloadFileActivityName, fileID).Get(sessionCtx, &fInfo)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar fInfoProcessed *fileInfo\n\terr = workflow.ExecuteActivity(sessionCtx, processFileActivityName, *fInfo).Get(sessionCtx, &fInfoProcessed)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = workflow.ExecuteActivity(sessionCtx, uploadFileActivityName, *fInfoProcessed).Get(sessionCtx, nil)\n\treturn err\n}\n"
  },
  {
    "path": "cmd/samples/fileprocessing/workflow_test.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/suite\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\ntype UnitTestSuite struct {\n\tsuite.Suite\n\ttestsuite.WorkflowTestSuite\n\n\tenv *testsuite.TestWorkflowEnvironment\n}\n\nfunc TestUnitTestSuite(t *testing.T) {\n\tsuite.Run(t, new(UnitTestSuite))\n}\n\nfunc (s *UnitTestSuite) SetupTest() {\n\ts.env = s.NewTestWorkflowEnvironment()\n\ts.env.RegisterWorkflow(sampleFileProcessingWorkflow)\n\ts.env.RegisterActivityWithOptions(downloadFileActivity, activity.RegisterOptions{\n\t\tName: downloadFileActivityName,\n\t})\n\ts.env.RegisterActivityWithOptions(processFileActivity, activity.RegisterOptions{\n\t\tName: processFileActivityName,\n\t})\n\ts.env.RegisterActivityWithOptions(uploadFileActivity, activity.RegisterOptions{\n\t\tName: uploadFileActivityName,\n\t})\n}\n\nfunc (s *UnitTestSuite) TearDownTest() {\n\ts.env.AssertExpectations(s.T())\n}\n\nfunc (s *UnitTestSuite) Test_SampleFileProcessingWorkflow() {\n\tfileID := \"test-file-id\"\n\texpectedCall := []string{\n\t\t\"downloadFileActivity\",\n\t\t\"processFileActivity\",\n\t\t\"uploadFileActivity\",\n\t}\n\n\tvar activityCalled []string\n\ts.env.SetOnActivityStartedListener(func(activityInfo *activity.Info, ctx context.Context, args encoded.Values) {\n\t\tactivityType := activityInfo.ActivityType.Name\n\t\tif strings.HasPrefix(activityType, \"internalSession\") {\n\t\t\treturn\n\t\t}\n\t\tactivityCalled = append(activityCalled, activityType)\n\t\tswitch activityType {\n\t\tcase expectedCall[0]:\n\t\t\tvar input string\n\t\t\ts.NoError(args.Get(&input))\n\t\t\ts.Equal(fileID, input)\n\t\tcase expectedCall[1]:\n\t\t\tvar input fileInfo\n\t\t\ts.NoError(args.Get(&input))\n\t\t\ts.Equal(input.HostID, HostID)\n\t\tcase expectedCall[2]:\n\t\t\tvar input fileInfo\n\t\t\ts.NoError(args.Get(&input))\n\t\t\ts.Equal(input.HostID, HostID)\n\t\tdefault:\n\t\t\tpanic(\"unexpected activity call\")\n\t\t}\n\t})\n\ts.env.ExecuteWorkflow(sampleFileProcessingWorkflow, fileID)\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n\ts.Equal(expectedCall, activityCalled)\n}\n"
  },
  {
    "path": "cmd/samples/pso/README.md",
    "content": "This sample workflow demos a long iterative math optimization process using particle swarm optimization (PSO). \n\nThe workflow first does some data structure initialization and then runs many iterations using a child workflow. The child workflow runs 10 iterations and then uses ContinueAsNew to avoid to store too long history in the Cadence database. In case of recovery the whole history has to be replayed to reconstruct the workflow state. So if history is too large the recover can take very long time.\nEach particle is processed in parallel using worflow.Go and the math grunt work is done in the activites.\nSince the data structure that maintains the optimization state has to be passed to the child workflow and the activities, a custom DataConverter has been implemented to take care of serialization/deserialization.\nAlso the query API is supported to get the current state of running workflow.\n\nSteps to run this sample: \n1) You need a cadence service running. See details in cmd/samples/README.md\n2) Run the following command multiple times on different console window. This is to simulate running workers on multiple different machines.\n```\n./bin/pso -m worker\n```\n3) Run the following command to submit a start request for this PSO workflow.\n```\n./bin/pso -m trigger\n```\n4) Query the state with\n```\n./bin/pso -m query -w <workflow_id from step 3> -r <run_id from step 3> -t state\n```\nReplace -t state with -t \\_\\_stack_trace to dump the call stack for the workflow.\n\nYou should see that all activities for one particular workflow execution are scheduled to run on one console window.\n"
  },
  {
    "path": "cmd/samples/pso/activities.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"math/rand\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/activity\"\n)\n\n/**\n * Sample activities used by file processing sample workflow.\n */\nconst (\n\tinitParticleActivityName   = \"initParticleActivityName\"\n\tupdateParticleActivityName = \"updateParticleActivityName\"\n)\n\nvar rng *rand.Rand\n\n// This is registration process where you register all your activity handlers.\nfunc init() {\n\t// initialize the RNG\n\t// WARNING: the randomness of activity scheduling with multiple workers makes random number generation truly random and not repeatable in debugging\n\t// worker.ReplayWorkflowHistoryFromJSONFile should be used to troubleshoot a specific workflow failure.\n\trng = rand.New(rand.NewSource(time.Now().UnixNano()))\n}\n\nfunc initParticleActivity(ctx context.Context, swarm Swarm) (Particle, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"initParticleActivity started.\")\n\n\tparticle := NewParticle(&swarm, rng)\n\tparticle.UpdateFitness(&swarm)\n\n\treturn *particle, nil\n}\n\nfunc updateParticleActivity(ctx context.Context, swarm Swarm, particleIdx int) (Particle, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"updateParticleActivity started.\")\n\n\tparticle := swarm.Particles[particleIdx]\n\tparticle.UpdateLocation(&swarm, rng)\n\tparticle.UpdateFitness(&swarm)\n\n\treturn *particle, nil\n}\n"
  },
  {
    "path": "cmd/samples/pso/dataconverter.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/gob\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"go.uber.org/cadence/encoded\"\n)\n\n// gobDataConverter implements encoded.DataConverter using gob for Swarm and Particle\n// WARGNING: Make sure all struct members are public (Capital letter) otherwise serialization does not work!\n// TODO: consider storing blobs in external DB or S3\ntype gobDataConverter struct {\n}\n\n// NewGobDataConverter creates a gob data converter\nfunc NewGobDataConverter() encoded.DataConverter {\n\treturn &gobDataConverter{}\n}\n\n// jsonDataConverter implements encoded.DataConverter using JSON for Swarm and Particle\n// WARGNING: Make sure all struct members are public (Capital letter) otherwise serialization does not work!\n// TODO: consider storing blobs in external DB or S3\ntype jsonDataConverter struct {\n}\n\n// NewJSONDataConverter creates a json data converter\nfunc NewJSONDataConverter() encoded.DataConverter {\n\treturn &jsonDataConverter{}\n}\n\n// Gob data converter implementation\n\nfunc (dc *gobDataConverter) ToData(value ...interface{}) ([]byte, error) {\n\tvar buf bytes.Buffer\n\tenc := gob.NewEncoder(&buf)\n\tvar err error\n\tfor i, obj := range value {\n\t\tswitch t := obj.(type) {\n\t\tcase Swarm:\n\t\t\terr = enc.Encode(*t.Settings)\n\t\t\tif err == nil {\n\t\t\t\terr = enc.Encode(*t.Gbest)\n\t\t\t\tif err == nil {\n\t\t\t\t\tif t.Settings.Size > 0 {\n\t\t\t\t\t\tfor _, particle := range t.Particles {\n\t\t\t\t\t\t\tif particle == nil {\n\t\t\t\t\t\t\t\tparticle = new(Particle)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\terr = enc.Encode(*particle)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tdefault:\n\t\t\terr = enc.Encode(obj)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\n\t\t\t\t\"unable to encode argument: %d, %v, with gob error: %v\", i, reflect.TypeOf(obj), err)\n\t\t}\n\t}\n\treturn buf.Bytes(), nil\n\t// TODO: store buf.Bytes() in DB/S3 and get key\n\t// return key, nil\n}\n\nfunc (dc *gobDataConverter) FromData(input []byte, valuePtr ...interface{}) error {\n\t// TODO: convert input into key in DB/S3 and retrieve bytes\n\t//dec := gob.NewDecoder(bytes)\n\tdec := gob.NewDecoder(bytes.NewBuffer(input))\n\tvar err error\n\tfor i, obj := range valuePtr {\n\t\tswitch t := obj.(type) {\n\t\tcase *Swarm:\n\t\t\tt.Settings = new(SwarmSettings)\n\t\t\terr = dec.Decode(t.Settings)\n\t\t\tt.Settings.function = FunctionFactory(t.Settings.FunctionName)\n\t\t\tt.Gbest = NewPosition(t.Settings.function.dim)\n\t\t\terr = dec.Decode(t.Gbest)\n\t\t\tt.Particles = make([]*Particle, t.Settings.Size)\n\t\t\tfor index := 0; index < t.Settings.Size; index++ {\n\t\t\t\tt.Particles[index] = new(Particle)\n\t\t\t\terr = dec.Decode(t.Particles[index])\n\t\t\t}\n\t\tdefault:\n\t\t\terr = dec.Decode(obj)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"unable to decode argument: %d, %v, with gob error: %v\", i, reflect.TypeOf(obj), err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// Json data converter implementation\n\nfunc (dc *jsonDataConverter) ToData(value ...interface{}) ([]byte, error) {\n\tvar buf bytes.Buffer\n\tenc := json.NewEncoder(&buf)\n\tvar err error\n\tfor i, obj := range value {\n\t\tswitch t := obj.(type) {\n\t\tcase Swarm:\n\t\t\terr = enc.Encode(*t.Settings)\n\t\t\tif err == nil {\n\t\t\t\terr = enc.Encode(*t.Gbest)\n\t\t\t\tif err == nil {\n\t\t\t\t\tif t.Settings.Size > 0 {\n\t\t\t\t\t\tfor _, particle := range t.Particles {\n\t\t\t\t\t\t\tif particle == nil {\n\t\t\t\t\t\t\t\tparticle = new(Particle)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\terr = enc.Encode(*particle)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tcase WorkflowResult:\n\t\t\terr = enc.Encode(t.Msg)\n\t\t\terr = enc.Encode(t.Success)\n\t\tdefault:\n\t\t\terr = enc.Encode(obj)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\n\t\t\t\t\"unable to encode argument: %d, %v, with error: %v\", i, reflect.TypeOf(obj), err)\n\t\t}\n\t}\n\treturn buf.Bytes(), nil\n\t// TODO: store buf.Bytes() in DB/S3 and get key\n\t// return key, nil\n}\n\nfunc (dc *jsonDataConverter) FromData(input []byte, valuePtr ...interface{}) error {\n\t// TODO: convert input into key in DB/S3 and retrieve bytes\n\t//dec := json.NewDecoder(bytes)\n\tdec := json.NewDecoder(bytes.NewBuffer(input))\n\tvar err error\n\tfor i, obj := range valuePtr {\n\t\tswitch t := obj.(type) {\n\t\tcase *Swarm:\n\t\t\tt.Settings = new(SwarmSettings)\n\t\t\terr = dec.Decode(t.Settings)\n\t\t\tt.Settings.function = FunctionFactory(t.Settings.FunctionName)\n\t\t\tt.Gbest = NewPosition(t.Settings.function.dim)\n\t\t\terr = dec.Decode(t.Gbest)\n\t\t\tt.Particles = make([]*Particle, t.Settings.Size)\n\t\t\tfor index := 0; index < t.Settings.Size; index++ {\n\t\t\t\tt.Particles[index] = new(Particle)\n\t\t\t\terr = dec.Decode(t.Particles[index])\n\t\t\t}\n\t\tcase *WorkflowResult:\n\t\t\terr = dec.Decode(&t.Msg)\n\t\t\terr = dec.Decode(&t.Success)\n\t\tdefault:\n\t\t\terr = dec.Decode(obj)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"unable to decode argument: %d, %v, with error: %v\", i, reflect.TypeOf(obj), err)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/samples/pso/functions.go",
    "content": "package main\n\nimport \"math\"\n\ntype ObjectiveFunction struct {\n\tname     string                      // name of the function\n\tdim      int                         // problem dimensionality\n\txLo      float64                     // lower range limit\n\txHi      float64                     // higher range limit\n\tGoal     float64                     // optimization goal (error threshold)\n\tEvaluate func(vec []float64) float64 // the objective function\n}\n\nvar Sphere = ObjectiveFunction{\n\tname:     \"sphere\",\n\tdim:      3,\n\txLo:      -100,\n\txHi:      100,\n\tGoal:     1e-5,\n\tEvaluate: EvalSphere,\n}\n\nvar Rosenbrock = ObjectiveFunction{\n\tname:     \"rosenbrock\",\n\tdim:      3,\n\txLo:      -2.048,\n\txHi:      2.048,\n\tGoal:     1e-5,\n\tEvaluate: EvalRosenbrock,\n}\nvar Griewank = ObjectiveFunction{\n\tname:     \"griewank\",\n\tdim:      3,\n\txLo:      -600,\n\txHi:      600,\n\tGoal:     1e-5,\n\tEvaluate: EvalGriewank,\n}\n\nfunc EvalSphere(vec []float64) float64 {\n\tvar sum float64 = 0\n\tfor i := 0; i < len(vec); i++ {\n\t\tsum += math.Pow(vec[i], 2.0)\n\t}\n\treturn sum\n}\n\nfunc EvalRosenbrock(vec []float64) float64 {\n\tvar sum float64 = 0\n\tfor i := 0; i < len(vec)-1; i++ {\n\t\tsum += 100.0*\n\t\t\tmath.Pow((vec[i+1]-math.Pow(vec[i], 2.0)), 2.0) +\n\t\t\tmath.Pow((1-vec[i]), 2.0)\n\t}\n\treturn sum\n}\n\nfunc EvalGriewank(vec []float64) float64 {\n\tvar sum float64 = 0\n\tvar prod float64 = 1\n\n\tfor i := 0; i < len(vec); i++ {\n\t\tsum += math.Pow(vec[i], 2.0)\n\t\tprod *= math.Cos(vec[i] / math.Sqrt(float64(i+1)))\n\t}\n\treturn sum/4000.0 - prod + 1.0\n}\n"
  },
  {
    "path": "cmd/samples/pso/main.go",
    "content": "package main\n\nimport (\n\t\"encoding/gob\"\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope:                       h.WorkerMetricScope,\n\t\tLogger:                             h.Logger,\n\t\tMaxConcurrentActivityExecutionSize: 1, // Activities are supposed to be CPU intensive, so better limit the concurrency\n\t\tDataConverter:                      h.DataConverter,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper, functionName string) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"PSO_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute * 60,\n\t\tDecisionTaskStartToCloseTimeout: time.Second * 10, // Measure of responsiveness of the worker to various server signals apart from start workflow. Small means faster recovery in the case of worker failure\n\t}\n\th.StartWorkflow(workflowOptions, samplePSOWorkflow, functionName)\n}\n\nfunc main() {\n\tvar mode, functionName, workflowID, runID, queryType string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger\")\n\tflag.StringVar(&functionName, \"f\", \"sphere\", \"One of [sphere, rosenbrock, griewank]\")\n\tflag.StringVar(&workflowID, \"w\", \"\", \"WorkflowID\")\n\tflag.StringVar(&runID, \"r\", \"\", \"RunID\")\n\tflag.StringVar(&queryType, \"t\", \"__stack_trace\", \"Query type is one of [__stack_trace, child, iteration]\")\n\tflag.Parse()\n\n\t// If Gob is used to serialize data, then need to register types into gob as well???\n\t// TOVERIFY: the test works even without type registation!\n\tconst useGob = false\n\tvar dataConverter encoded.DataConverter\n\tif useGob {\n\t\tdataConverter = NewGobDataConverter()\n\t\tgob.Register(Vector{})\n\t\tgob.Register(Position{})\n\t\tgob.Register(Particle{})\n\t\tgob.Register(ObjectiveFunction{})\n\t\tgob.Register(SwarmSettings{})\n\t\tgob.Register(Swarm{})\n\t} else {\n\t\tdataConverter = NewJSONDataConverter()\n\t}\n\n\tvar h common.SampleHelper\n\th.DataConverter = dataConverter\n\th.SetupServiceConfig() // This configures DataConverter\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(samplePSOWorkflow)\n\t\th.RegisterWorkflow(samplePSOChildWorkflow)\n\t\th.RegisterActivityWithAlias(initParticleActivity, initParticleActivityName)\n\t\th.RegisterActivityWithAlias(updateParticleActivity, updateParticleActivityName)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h, functionName)\n\tcase \"query\":\n\t\th.QueryWorkflow(workflowID, runID, queryType)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/pso/particle.go",
    "content": "package main\n\nimport \"math/rand\"\n\ntype Particle struct {\n\tPosition *Position\n\tPbest    *Position\n\tVelocity Vector\n}\n\nfunc NewParticle(swarm *Swarm, rng *rand.Rand) *Particle {\n\tparticle := new(Particle)\n\tparticle.Position = RandomPosition(swarm.Settings.function, rng)\n\n\tparticle.Pbest = particle.Position.Copy()\n\tparticle.Pbest.Fitness = 1e20\n\n\tparticle.Velocity = make([]float64, swarm.Settings.function.dim)\n\txLo := swarm.Settings.function.xLo\n\txHi := swarm.Settings.function.xHi\n\tfor i := 0; i < swarm.Settings.function.dim; i++ {\n\t\ta := xLo + (xHi-xLo)*rng.Float64()\n\t\tb := xLo + (xHi-xLo)*rng.Float64()\n\t\tparticle.Velocity[i] = (a - b) / 2.0\n\t}\n\treturn particle\n}\n\nfunc (particle *Particle) UpdateLocation(swarm *Swarm, rng *rand.Rand) {\n\tfor i := 0; i < swarm.Settings.function.dim; i++ {\n\t\t// calculate stochastic coefficients\n\t\trho1 := swarm.Settings.C1 * rng.Float64()\n\t\trho2 := swarm.Settings.C2 * rng.Float64()\n\t\t// update velocity\n\t\tparticle.Velocity[i] =\n\t\t\tswarm.Settings.Inertia*particle.Velocity[i] +\n\t\t\t\trho1*(particle.Pbest.Location[i]-particle.Position.Location[i]) +\n\t\t\t\trho2*(swarm.Gbest.Location[i]-particle.Position.Location[i])\n\n\t\tparticle.Position.Location[i] += particle.Velocity[i]\n\t}\n}\n\nfunc (particle *Particle) UpdateFitness(swarm *Swarm) {\n\tparticle.Position.Fitness = swarm.Settings.function.Evaluate(particle.Position.Location)\n\n\tif particle.Position.IsBetterThan(particle.Pbest) {\n\t\tparticle.Pbest = particle.Position.Copy()\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/pso/position.go",
    "content": "package main\n\nimport (\n\t\"math/rand\"\n)\n\ntype Vector []float64\n\ntype Position struct {\n\tLocation Vector\n\tFitness  float64\n}\n\nfunc NewPosition(dim int) *Position {\n\tloc := make([]float64, dim)\n\treturn &Position{\n\t\tLocation: loc,\n\t\t// Fitness:  EvaluateFunction(settings.Function.Evaluate, loc),\n\t}\n}\n\nfunc RandomPosition(function ObjectiveFunction, rng *rand.Rand) *Position {\n\tpos := NewPosition(function.dim)\n\txLo := function.xLo\n\txHi := function.xHi\n\tfor i := 0; i < len(pos.Location); i++ {\n\t\tpos.Location[i] = xLo + (xHi-xLo)*rng.Float64()\n\t}\n\t// pos.Fitness = EvaluateFunction(settings.Function.Evaluate, pos.Location)\n\treturn pos\n}\n\nfunc (position *Position) Copy() *Position {\n\tnewPosition := NewPosition(len(position.Location))\n\tcopy(newPosition.Location, position.Location)\n\tnewPosition.Fitness = position.Fitness\n\treturn newPosition\n}\n\nfunc (position *Position) IsBetterThan(other *Position) bool {\n\treturn position.Fitness < other.Fitness\n}\n"
  },
  {
    "path": "cmd/samples/pso/settings.go",
    "content": "package main\n\nconst pso_max_size int = 100\nconst pso_inertia float64 = 0.7298 // default value of w (see clerc02)\n\ntype SwarmSettings struct {\n\tFunctionName string\n\tfunction     ObjectiveFunction // lower case to avoid data converter export\n\t// swarm size (number of particles)\n\tSize int\n\t// ... N steps (set to 0 for no output)\n\tPrintEvery int\n\t// Steps after issuing a ContinueAsNew, to reduce history size\n\tContinueAsNewEvery int\n\t// maximum number of iterations\n\tSteps int\n\t// cognitive coefficient\n\tC1 float64\n\t// social coefficient\n\tC2 float64\n\t// max inertia weight value\n\tInertiaMax float64\n\t// min inertia weight value\n\tInertiaMin float64\n\t// whether to keep particle position within defined bounds (TRUE)\n\t// or apply periodic boundary conditions (FALSE)\n\tClampPosition bool\n\n\tInertia float64 // current inertia weight value\n}\n\nfunc FunctionFactory(functionName string) ObjectiveFunction {\n\tvar function ObjectiveFunction\n\tswitch functionName {\n\tcase \"sphere\":\n\t\tfunction = Sphere\n\tcase \"rosenbrock\":\n\t\tfunction = Rosenbrock\n\tcase \"griewank\":\n\t\tfunction = Griewank\n\t}\n\treturn function\n}\n\nfunc PSODefaultSettings(functionName string) *SwarmSettings {\n\tsettings := new(SwarmSettings)\n\n\tsettings.FunctionName = functionName\n\tsettings.function = FunctionFactory(functionName)\n\n\tsettings.Size = CalculateSwarmSize(settings.function.dim, pso_max_size)\n\tsettings.PrintEvery = 10\n\tsettings.ContinueAsNewEvery = 10\n\tsettings.Steps = 100000\n\tsettings.C1 = 1.496\n\tsettings.C2 = 1.496\n\tsettings.InertiaMax = pso_inertia\n\tsettings.InertiaMin = 0.3\n\tsettings.Inertia = settings.InertiaMax\n\n\tsettings.ClampPosition = true\n\n\treturn settings\n}\n"
  },
  {
    "path": "cmd/samples/pso/swarm.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\ntype ParticleResult struct {\n\tPosition\n\tStep int\n}\n\ntype Swarm struct {\n\tSettings  *SwarmSettings\n\tGbest     *Position\n\tParticles []*Particle\n}\n\nfunc NewSwarm(ctx workflow.Context, settings *SwarmSettings) (*Swarm, error) {\n\tvar swarm Swarm\n\t// store settings\n\tswarm.Settings = settings\n\t// initialize gbest\n\tswarm.Gbest = NewPosition(swarm.Settings.function.dim)\n\tswarm.Gbest.Fitness = 1e20\n\n\t// initialize particles in parallel\n\tchunkResultChannel := workflow.NewChannel(ctx)\n\tswarm.Particles = make([]*Particle, settings.Size)\n\tfor i := 0; i < swarm.Settings.Size; i++ {\n\t\tparticleIdx := i\n\t\tworkflow.Go(ctx, func(ctx workflow.Context) {\n\t\t\tvar particle Particle\n\t\t\terr := workflow.ExecuteActivity(ctx, initParticleActivityName, swarm).Get(ctx, &particle)\n\t\t\tif err == nil {\n\t\t\t\tswarm.Particles[particleIdx] = &particle\n\t\t\t}\n\t\t\tchunkResultChannel.Send(ctx, err)\n\t\t})\n\t}\n\n\t// wait for all particles to be initialized\n\tfor i := 0; i < swarm.Settings.Size; i++ {\n\t\tvar v interface{}\n\t\tchunkResultChannel.Receive(ctx, &v)\n\t\tswitch r := v.(type) {\n\t\tcase error:\n\t\t\tif r != nil {\n\t\t\t\treturn &swarm, r\n\t\t\t}\n\t\t}\n\t}\n\n\tswarm.updateBest()\n\n\treturn &swarm, nil\n}\n\nfunc (swarm *Swarm) updateBest() {\n\tfor i := 0; i < swarm.Settings.Size; i++ {\n\t\tif swarm.Particles[i].Pbest.IsBetterThan(swarm.Gbest) {\n\t\t\tswarm.Gbest = swarm.Particles[i].Pbest.Copy()\n\t\t}\n\t}\n}\n\nfunc (swarm *Swarm) Run(ctx workflow.Context, step int) (ParticleResult, error) {\n\tlogger := workflow.GetLogger(ctx)\n\n\t// Setup query handler for query type \"iteration\"\n\tvar iterationMessage string\n\terr := workflow.SetQueryHandler(ctx, \"iteration\", func(input []byte) (string, error) {\n\t\treturn iterationMessage, nil\n\t})\n\tif err != nil {\n\t\tlogger.Info(\"SetQueryHandler failed: \" + err.Error())\n\t\treturn ParticleResult{}, err\n\t}\n\n\t// the algorithm goes here\n\tchunkResultChannel := workflow.NewChannel(ctx)\n\tfor step <= swarm.Settings.Steps {\n\t\tlogger.Info(\"Iteration \", zap.String(\"step\", strconv.Itoa(step)))\n\t\t// Update particles in parallel\n\t\tfor i := 0; i < swarm.Settings.Size; i++ {\n\t\t\tparticleIdx := i\n\t\t\tworkflow.Go(ctx, func(ctx workflow.Context) {\n\t\t\t\tvar particle Particle\n\t\t\t\terr := workflow.ExecuteActivity(ctx, updateParticleActivityName, *swarm, particleIdx).Get(ctx, &particle)\n\t\t\t\tif err == nil {\n\t\t\t\t\tswarm.Particles[particleIdx] = &particle\n\t\t\t\t}\n\t\t\t\tchunkResultChannel.Send(ctx, err)\n\t\t\t})\n\t\t}\n\n\t\t// Wait for all particles to be updated\n\t\tfor i := 0; i < swarm.Settings.Size; i++ {\n\t\t\tvar v interface{}\n\t\t\tchunkResultChannel.Receive(ctx, &v)\n\t\t\tswitch r := v.(type) {\n\t\t\tcase error:\n\t\t\t\tif r != nil {\n\t\t\t\t\treturn ParticleResult{\n\t\t\t\t\t\tPosition: *swarm.Gbest,\n\t\t\t\t\t\tStep:     step,\n\t\t\t\t\t}, r\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlogger.Debug(\"Iteration Update Swarm Best\", zap.String(\"step\", strconv.Itoa(step)))\n\n\t\tswarm.updateBest()\n\n\t\t// Check if the goal has reached then stop early\n\t\tif swarm.Gbest.Fitness < swarm.Settings.function.Goal {\n\t\t\tlogger.Debug(\"Iteration New Swarm Best\", zap.String(\"step\", strconv.Itoa(step)))\n\t\t\treturn ParticleResult{\n\t\t\t\tPosition: *swarm.Gbest,\n\t\t\t\tStep:     step,\n\t\t\t}, nil\n\t\t}\n\n\t\titerationMessage = fmt.Sprintf(\"Step %d :: min err=%.5e\\n\", step, swarm.Gbest.Fitness)\n\t\tif step%swarm.Settings.PrintEvery == 0 {\n\t\t\tlogger.Info(iterationMessage)\n\t\t}\n\n\t\t// Finished all iterations\n\t\tif step == swarm.Settings.Steps {\n\t\t\tbreak\n\t\t}\n\n\t\t// Not finished yet, just continue as new to reduce history size\n\t\tif step%swarm.Settings.ContinueAsNewEvery == 0 {\n\t\t\treturn ParticleResult{\n\t\t\t\tPosition: *swarm.Gbest,\n\t\t\t\tStep:     step,\n\t\t\t}, errors.New(ContinueAsNewStr)\n\t\t}\n\n\t\tstep++\n\t}\n\n\treturn ParticleResult{\n\t\tPosition: *swarm.Gbest,\n\t\tStep:     step,\n\t}, nil\n}\n"
  },
  {
    "path": "cmd/samples/pso/utils.go",
    "content": "package main\n\nimport (\n\t\"math\"\n)\n\nfunc CalculateSwarmSize(dim, max_size int) int {\n\ts := 10. + 2.*math.Sqrt(float64(dim))\n\tsize := int(math.Floor(s + 0.5))\n\tif size > max_size {\n\t\treturn max_size\n\t} else {\n\t\treturn size\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/pso/workflow.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence\"\n\t\"go.uber.org/cadence/workflow\"\n)\n\ntype WorkflowResult struct {\n\tMsg     string // Uppercase the members otherwise serialization won't work!\n\tSuccess bool\n}\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"PSO\"\n\n// ActivityOptions can be reused\nvar ActivityOptions = workflow.ActivityOptions{\n\tScheduleToStartTimeout: time.Second * 5,\n\tStartToCloseTimeout:    time.Minute * 10,\n\tHeartbeatTimeout:       time.Second * 2, // such a short timeout to make sample fail over very fast\n\tRetryPolicy: &cadence.RetryPolicy{\n\t\tInitialInterval:          time.Second,\n\t\tBackoffCoefficient:       2.0,\n\t\tMaximumInterval:          time.Minute,\n\t\tExpirationInterval:       time.Minute * 10,\n\t\tMaximumAttempts:          5,\n\t\tNonRetriableErrorReasons: []string{\"bad-error\"},\n\t},\n}\n\nconst ContinueAsNewStr = \"CONTINUEASNEW\"\n\n//samplePSOWorkflow workflow decider\nfunc samplePSOWorkflow(ctx workflow.Context, functionName string) (string, error) {\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(fmt.Sprintf(\"Optimizing function %s\", functionName))\n\n\t// Set activity options\n\tctx = workflow.WithActivityOptions(ctx, ActivityOptions)\n\n\t// Setup query handler for query type \"child\"\n\tvar childWorkflowID string\n\terr := workflow.SetQueryHandler(ctx, \"child\", func(input []byte) (string, error) {\n\t\treturn childWorkflowID, nil\n\t})\n\tif err != nil {\n\t\tmsg := fmt.Sprintf(\"SetQueryHandler failed: \" + err.Error())\n\t\tlogger.Error(msg)\n\t\treturn msg, err\n\t}\n\n\t// Retry with different random seed\n\tsettings := PSODefaultSettings(functionName)\n\tconst NumberOfAttempts = 5\n\tfor i := 1; i < NumberOfAttempts; i++ {\n\t\tlogger.Info(fmt.Sprintf(\"Attempt #%d\", i))\n\n\t\tswarm, err := NewSwarm(ctx, settings)\n\t\tif err != nil {\n\t\t\tmsg := fmt.Sprintf(\"Optimization failed. \" + err.Error())\n\t\t\tlogger.Error(msg)\n\t\t\treturn msg, err\n\t\t}\n\n\t\t// Set child workflow options\n\t\t// Parent workflow can choose to specify it's own ID for child execution.  Make sure they are unique for each execution.\n\t\tcwo := workflow.ChildWorkflowOptions{\n\t\t\tWorkflowID:                   \"PSO_Child_\" + uuid.New(),\n\t\t\tExecutionStartToCloseTimeout: time.Minute,\n\t\t}\n\t\tctx = workflow.WithChildOptions(ctx, cwo)\n\n\t\tchildWorkflowFuture := workflow.ExecuteChildWorkflow(ctx, samplePSOChildWorkflow, *swarm, 1)\n\t\tvar childWE workflow.Execution\n\t\tchildWorkflowFuture.GetChildWorkflowExecution().Get(ctx, &childWE)\n\t\tchildWorkflowID = childWE.ID\n\t\tvar result WorkflowResult\n\t\terr = childWorkflowFuture.Get(ctx, &result) // This blocking until the child workflow has finished\n\t\tif err != nil {\n\t\t\tmsg := fmt.Sprintf(\"Parent execution received child execution failure. \" + err.Error())\n\t\t\tlogger.Error(msg)\n\t\t\treturn msg, err\n\t\t}\n\t\tif result.Success {\n\t\t\tmsg := fmt.Sprintf(\"Optimization was successful at attempt #%d. %s\", i, result.Msg)\n\t\t\tlogger.Info(msg)\n\t\t\treturn msg, nil\n\t\t}\n\t}\n\n\tmsg := fmt.Sprintf(\"Unable to reach goal after %d attempts\", NumberOfAttempts)\n\tlogger.Info(msg)\n\treturn msg, nil\n}\n\n// samplePSOChildWorkflow workflow decider\n// Returns true if the optimization has converged\nfunc samplePSOChildWorkflow(ctx workflow.Context, swarm Swarm, startingStep int) (WorkflowResult, error) {\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"Child workflow execution started.\")\n\n\t// Set activity options\n\tctx = workflow.WithActivityOptions(ctx, ActivityOptions)\n\n\t// Run real optimization loop\n\tresult, err := swarm.Run(ctx, startingStep)\n\tif err != nil {\n\t\tif err.Error() == ContinueAsNewStr {\n\t\t\treturn WorkflowResult{\"NewContinueAsNewError\", false}, workflow.NewContinueAsNewError(ctx, samplePSOChildWorkflow, swarm, result.Step+1)\n\t\t}\n\n\t\tmsg := fmt.Sprintf(\"Error in swarm loop: \" + err.Error())\n\t\tlogger.Error(msg)\n\t\treturn WorkflowResult{msg, false}, errors.New(\"Error in swarm loop\")\n\t}\n\tif result.Position.Fitness < swarm.Settings.function.Goal {\n\t\tmsg := fmt.Sprintf(\"Yay! Goal was reached @ step %d (fitness=%.2e) :-)\", result.Step, result.Position.Fitness)\n\t\tlogger.Info(msg)\n\t\treturn WorkflowResult{msg, true}, nil\n\t}\n\n\tmsg := fmt.Sprintf(\"Goal was not reached after %d steps (fitness=%.2e) :-)\", result.Step, result.Position.Fitness)\n\tlogger.Info(msg)\n\treturn WorkflowResult{msg, false}, nil\n}\n"
  },
  {
    "path": "cmd/samples/pso/workflow_test.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/testsuite\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n)\n\nfunc Test_Workflow(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\tenv := testSuite.NewTestWorkflowEnvironment()\n\tenv.RegisterWorkflow(samplePSOWorkflow)\n\tenv.RegisterWorkflow(samplePSOChildWorkflow)\n\tenv.RegisterActivityWithOptions(initParticleActivity, activity.RegisterOptions{\n\t\tName: initParticleActivityName,\n\t})\n\tenv.RegisterActivityWithOptions(updateParticleActivity, activity.RegisterOptions{\n\t\tName: updateParticleActivityName,\n\t})\n\n\tvar activityCalled []string\n\n\t//var dataConverter = NewGobDataConverter()\n\tvar dataConverter = NewJSONDataConverter()\n\tworkerOptions := worker.Options{\n\t\tDataConverter: dataConverter,\n\t}\n\tenv.SetWorkerOptions(workerOptions)\n\n\t// env.SetWorkflowTimeout(time.Minute * 5)\n\t// env.SetTestTimeout(time.Minute * 5)\n\n\tenv.SetOnActivityStartedListener(func(activityInfo *activity.Info, ctx context.Context, args encoded.Values) {\n\t\tactivityType := activityInfo.ActivityType.Name\n\t\tactivityCalled = append(activityCalled, activityType)\n\t\tswitch activityType {\n\t\tcase \"initParticleActivityName\":\n\t\tcase \"updateParticleActivityName\":\n\t\tdefault:\n\t\t\tpanic(\"unexpected activity call\")\n\t\t}\n\t})\n\n\tvar childWorkflowID string\n\tenv.SetOnChildWorkflowStartedListener(func(workflowInfo *workflow.Info, ctx workflow.Context, args encoded.Values) {\n\t\tchildWorkflowID = workflowInfo.WorkflowExecution.ID\n\t})\n\n\tenv.ExecuteWorkflow(samplePSOWorkflow, \"sphere\")\n\n\trequire.True(t, env.IsWorkflowCompleted())\n\tqueryAndVerify(t, env, \"child\", childWorkflowID)\n\t//queryAndVerify(t, env, \"iteration\", \"???\")\n\trequire.Equal(t, env.GetWorkflowError().Error(), \"ContinueAsNew\") // consider recreating a new test env on every iteration and calling execute workflow with the arguments from the previous iteration (contained in ContinueAsNewError)\n}\n\nfunc queryAndVerify(t *testing.T, env *testsuite.TestWorkflowEnvironment, query string, expectedState string) {\n\tresult, err := env.QueryWorkflow(query)\n\trequire.NoError(t, err)\n\tvar state string\n\terr = result.Get(&state)\n\trequire.NoError(t, err)\n\trequire.Equal(t, expectedState, state)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/branch/branch_workflow.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/workflow\"\n)\n\n/**\n * This sample workflow executes multiple branches in parallel. The number of branches is controlled by passed in parameter.\n */\n\nconst (\n\t// ApplicationName is the task list for this sample\n\tApplicationName = \"branchGroup\"\n\n\ttotalBranches = 3\n)\n\n// sampleBranchWorkflow workflow decider\nfunc sampleBranchWorkflow(ctx workflow.Context) error {\n\tvar futures []workflow.Future\n\t// starts activities in parallel\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tfor i := 1; i <= totalBranches; i++ {\n\t\tactivityInput := fmt.Sprintf(\"branch %d of %d.\", i, totalBranches)\n\t\tfuture := workflow.ExecuteActivity(ctx, sampleActivity, activityInput)\n\t\tfutures = append(futures, future)\n\t}\n\n\t// wait until all futures are done\n\tfor _, future := range futures {\n\t\tif err := future.Get(ctx, nil); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tworkflow.GetLogger(ctx).Info(\"Workflow completed.\")\n\n\treturn nil\n}\n\nfunc sampleActivity(input string) (string, error) {\n\tname := \"sampleActivity\"\n\tfmt.Printf(\"Run %s with input %v \\n\", name, input)\n\treturn \"Result_\" + name, nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/branch/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflowParallel(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"parallel_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, sampleParallelWorkflow)\n}\n\nfunc startWorkflowBranch(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"branch_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, sampleBranchWorkflow)\n}\n\nfunc main() {\n\tvar mode, sampleCase string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.StringVar(&sampleCase, \"c\", \"\", \"Sample case to run.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(sampleBranchWorkflow)\n\t\th.RegisterWorkflow(sampleParallelWorkflow)\n\t\th.RegisterActivity(sampleActivity)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tswitch sampleCase {\n\t\tcase \"branch\":\n\t\t\tstartWorkflowBranch(&h)\n\t\tdefault:\n\t\t\tstartWorkflowParallel(&h)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/branch/parallel_workflow.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * This sample workflow executes multiple branches in parallel using workflow.Go() method.\n */\n\n// sampleParallelWorkflow workflow decider\nfunc sampleParallelWorkflow(ctx workflow.Context) error {\n\twaitChannel := workflow.NewChannel(ctx)\n\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tlogger := workflow.GetLogger(ctx)\n\tworkflow.Go(ctx, func(ctx workflow.Context) {\n\t\terr := workflow.ExecuteActivity(ctx, sampleActivity, \"branch1.1\").Get(ctx, nil)\n\t\tif err != nil {\n\t\t\tlogger.Error(\"Activity failed\", zap.Error(err))\n\t\t\twaitChannel.Send(ctx, err.Error())\n\t\t\treturn\n\t\t}\n\t\terr = workflow.ExecuteActivity(ctx, sampleActivity, \"branch1.2\").Get(ctx, nil)\n\t\tif err != nil {\n\t\t\tlogger.Error(\"Activity failed\", zap.Error(err))\n\t\t\twaitChannel.Send(ctx, err.Error())\n\t\t\treturn\n\t\t}\n\t\twaitChannel.Send(ctx, \"\")\n\t})\n\n\tworkflow.Go(ctx, func(ctx workflow.Context) {\n\t\terr := workflow.ExecuteActivity(ctx, sampleActivity, \"branch2\").Get(ctx, nil)\n\t\tif err != nil {\n\t\t\tlogger.Error(\"Activity failed\", zap.Error(err))\n\t\t\twaitChannel.Send(ctx, err.Error())\n\t\t\treturn\n\t\t}\n\t\twaitChannel.Send(ctx, \"\")\n\t})\n\n\t// wait for both of the coroutinue to complete.\n\tvar errMsg string\n\tfor i := 0; i != 2; i++ {\n\t\twaitChannel.Receive(ctx, &errMsg)\n\t\tif errMsg != \"\" {\n\t\t\terr := errors.New(errMsg)\n\t\t\tlogger.Error(\"Coroutine failed\", zap.Error(err))\n\t\t\treturn err\n\t\t}\n\t}\n\n\tlogger.Info(\"Workflow completed.\")\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/branch/workflow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/suite\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\ntype UnitTestSuite struct {\n\tsuite.Suite\n\ttestsuite.WorkflowTestSuite\n\n\tenv *testsuite.TestWorkflowEnvironment\n}\n\nfunc TestUnitTestSuite(t *testing.T) {\n\tsuite.Run(t, new(UnitTestSuite))\n}\n\nfunc (s *UnitTestSuite) SetupTest() {\n\ts.env = s.NewTestWorkflowEnvironment()\n\ts.env.RegisterWorkflow(sampleBranchWorkflow)\n\ts.env.RegisterWorkflow(sampleParallelWorkflow)\n\ts.env.RegisterActivity(sampleActivity)\n}\n\nfunc (s *UnitTestSuite) TearDownTest() {\n\ts.env.AssertExpectations(s.T())\n}\n\nfunc (s *UnitTestSuite) Test_BranchWorkflow() {\n\ts.env.ExecuteWorkflow(sampleBranchWorkflow)\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n}\n\nfunc (s *UnitTestSuite) Test_ParallelWorkflow() {\n\ts.env.ExecuteWorkflow(sampleParallelWorkflow)\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n}\n"
  },
  {
    "path": "cmd/samples/recipes/cancelactivity/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"cancel_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute * 30,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, sampleCancelWorkflow)\n}\n\nfunc cancelWorkflow(h *common.SampleHelper, wid string) {\n\th.CancelWorkflow(wid)\n}\n\nfunc main() {\n\tvar mode, wid string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker, trigger or cancel.\")\n\tflag.StringVar(&wid, \"w\", \"<workflowID>\", \"w is the workflowID of the workflow to be canceled.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(sampleCancelWorkflow)\n\t\th.RegisterActivity(activityToBeCanceled)\n\t\th.RegisterActivity(activityToBeSkipped)\n\t\th.RegisterActivity(cleanupActivity)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\tcase \"cancel\":\n\t\tcancelWorkflow(&h, wid)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/cancelactivity/workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.uber.org/cadence\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * This is the cancel activity workflow sample.\n */\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"cancelGroup\"\n\n// sampleCancelWorkflow workflow decider\nfunc sampleCancelWorkflow(ctx workflow.Context) (retError error) {\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute * 30,\n\t\tHeartbeatTimeout:       time.Second * 5,\n\t\tWaitForCancellation:    true,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"cancel workflow started\")\n\n\tdefer func() {\n\t\tif cadence.IsCanceledError(retError) {\n\t\t\t// When workflow is canceled, it has to get a new disconnected context to execute any activities\n\t\t\tnewCtx, _ := workflow.NewDisconnectedContext(ctx)\n\t\t\terr := workflow.ExecuteActivity(newCtx, cleanupActivity).Get(ctx, nil)\n\t\t\tif err != nil {\n\t\t\t\tlogger.Error(\"Cleanup activity failed\", zap.Error(err))\n\t\t\t\tretError = err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tretError = nil\n\t\t\tlogger.Info(\"Workflow completed.\")\n\t\t}\n\t}()\n\n\tvar result string\n\terr := workflow.ExecuteActivity(ctx, activityToBeCanceled).Get(ctx, &result)\n\tif err != nil && !cadence.IsCanceledError(err) {\n\t\tlogger.Error(\"Error from activityToBeCanceled\", zap.Error(err))\n\t\treturn err\n\t}\n\tlogger.Info(fmt.Sprintf(\"activityToBeCanceled returns %v, %v\", result, err))\n\n\t// Execute activity using a canceled ctx,\n\t// activity won't be scheduled and an cancelled error will be returned\n\terr = workflow.ExecuteActivity(ctx, activityToBeSkipped).Get(ctx, nil)\n\tif err != nil && !cadence.IsCanceledError(err) {\n\t\tlogger.Error(\"Error from activityToBeSkipped\", zap.Error(err))\n\t}\n\n\treturn err\n}\n\nfunc activityToBeCanceled(ctx context.Context) (string, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"activity started, to cancel workflow, use ./cancelactivity -m cancel -w <WorkflowID> or CLI: 'cadence --do default wf cancel -w <WorkflowID>' to cancel\")\n\tfor {\n\t\tselect {\n\t\tcase <-time.After(1 * time.Second):\n\t\t\tlogger.Info(\"heartbeating...\")\n\t\t\tactivity.RecordHeartbeat(ctx, \"\")\n\t\tcase <-ctx.Done():\n\t\t\tlogger.Info(\"context is cancelled\")\n\t\t\t// returned canceled error here so that in workflow history we can see ActivityTaskCanceled event\n\t\t\t// or if not cancelled, return timeout error\n\t\t\treturn \"I am canceled by Done\", ctx.Err()\n\t\t}\n\t}\n}\n\nfunc cleanupActivity(ctx context.Context) error {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"cleanupActivity started\")\n\treturn nil\n}\n\nfunc activityToBeSkipped(ctx context.Context) error {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"this activity will be skipped due to cancellation\")\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/cancelactivity/workflow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\nfunc TestCancelWorkflow(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\n\tcanceledActivityName := \"canceledActivity\"\n\tskippedActivityName := \"skippedActivity\"\n\tcleanupActivityName := \"cleanupActivity\"\n\n\tenv := testSuite.NewTestWorkflowEnvironment()\n\tenv.RegisterWorkflow(sampleCancelWorkflow)\n\tenv.RegisterActivityWithOptions(activityToBeCanceled, activity.RegisterOptions{\n\t\tName: canceledActivityName,\n\t})\n\tenv.RegisterActivityWithOptions(activityToBeSkipped, activity.RegisterOptions{\n\t\tName: skippedActivityName,\n\t})\n\tenv.RegisterActivityWithOptions(cleanupActivity, activity.RegisterOptions{\n\t\tName: cleanupActivityName,\n\t})\n\n\tcanceledActivityCanceled := false\n\tenv.SetOnActivityCanceledListener(func(activityInfo *activity.Info) {\n\t\tif activityInfo.ActivityType.Name == canceledActivityName {\n\t\t\tcanceledActivityCanceled = true\n\t\t}\n\t})\n\n\tskippedActivityExecuted := false\n\tcleanupActivityCompleted := false\n\tenv.SetOnActivityCompletedListener(func(activityInfo *activity.Info, result encoded.Value, err error) {\n\t\tswitch activityInfo.ActivityType.Name {\n\t\tcase cleanupActivityName:\n\t\t\tcleanupActivityCompleted = true\n\t\tcase skippedActivityName:\n\t\t\tskippedActivityExecuted = true\n\t\t}\n\t})\n\n\tenv.RegisterDelayedCallback(func() {\n\t\tenv.CancelWorkflow()\n\t}, time.Second)\n\n\tenv.ExecuteWorkflow(sampleCancelWorkflow)\n\n\trequire.True(t, env.IsWorkflowCompleted())\n\trequire.NoError(t, env.GetWorkflowError())\n\trequire.True(t, canceledActivityCanceled)\n\trequire.False(t, skippedActivityExecuted)\n\trequire.True(t, cleanupActivityCompleted)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/childworkflow/child_workflow.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * This sample workflow demonstrates how to use invoke child workflow from parent workflow execution.  Each child\n * workflow execution is starting a new run and parent execution is notified only after the completion of last run.\n */\n\n// sampleChildWorkflow workflow decider\nfunc sampleChildWorkflow(ctx workflow.Context, totalCount, runCount int) (string, error) {\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"Child workflow execution started.\")\n\tif runCount <= 0 {\n\t\tlogger.Error(\"Invalid valid for run count.\", zap.Int(\"RunCount\", runCount))\n\t\treturn \"\", errors.New(\"invalid run count\")\n\t}\n\n\ttotalCount++\n\trunCount--\n\tif runCount == 0 {\n\t\tresult := fmt.Sprintf(\"Child workflow execution completed after %v runs\", totalCount)\n\t\tlogger.Info(\"Child workflow completed.\", zap.String(\"Result\", result))\n\t\treturn result, nil\n\t}\n\n\tlogger.Info(\"Child workflow starting new run.\", zap.Int(\"RunCount\", runCount), zap.Int(\"TotalCount\",\n\t\ttotalCount))\n\treturn \"\", workflow.NewContinueAsNewError(ctx, sampleChildWorkflow, totalCount, runCount)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/childworkflow/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"parentworkflow_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, sampleParentWorkflow)\n}\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(sampleChildWorkflow)\n\t\th.RegisterWorkflow(sampleParentWorkflow)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/childworkflow/parent_workflow.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * This sample workflow demonstrates how to use invoke child workflow from parent workflow execution.  Each child\n * workflow execution is starting a new run and parent execution is notified only after the completion of last run.\n */\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"childWorkflowGroup\"\n\n// sampleParentWorkflow workflow decider\nfunc sampleParentWorkflow(ctx workflow.Context) error {\n\tlogger := workflow.GetLogger(ctx)\n\texecution := workflow.GetInfo(ctx).WorkflowExecution\n\t// Parent workflow can choose to specify it's own ID for child execution.  Make sure they are unique for each execution.\n\tchildID := fmt.Sprintf(\"child_workflow:%v\", execution.RunID)\n\tcwo := workflow.ChildWorkflowOptions{\n\t\t// Do not specify WorkflowID if you want cadence to generate a unique ID for child execution\n\t\tWorkflowID:                   childID,\n\t\tExecutionStartToCloseTimeout: time.Minute,\n\t}\n\tctx = workflow.WithChildOptions(ctx, cwo)\n\tvar result string\n\terr := workflow.ExecuteChildWorkflow(ctx, sampleChildWorkflow, 0, 5).Get(ctx, &result)\n\tif err != nil {\n\t\tlogger.Error(\"Parent execution received child execution failure.\", zap.Error(err))\n\t\treturn err\n\t}\n\n\tlogger.Info(\"Parent execution completed.\", zap.String(\"Result\", result))\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/choice/exclusive_choice_workflow.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * This sample workflow Execute one of many code paths based on the result of an activity.\n */\n\nconst (\n\t// ApplicationName is the task list for this sample\n\tApplicationName = \"choiceGroup\"\n\n\torderChoiceApple  = \"apple\"\n\torderChoiceBanana = \"banana\"\n\torderChoiceCherry = \"cherry\"\n\torderChoiceOrange = \"orange\"\n)\n\nvar _orderChoices = []string{orderChoiceApple, orderChoiceBanana, orderChoiceCherry, orderChoiceOrange}\n\n// exclusiveChoiceWorkflow Workflow Decider.\nfunc exclusiveChoiceWorkflow(ctx workflow.Context) error {\n\t// Get order.\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tvar orderChoice string\n\terr := workflow.ExecuteActivity(ctx, getOrderActivity).Get(ctx, &orderChoice)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tlogger := workflow.GetLogger(ctx)\n\n\t// choose next activity based on order result\n\tswitch orderChoice {\n\tcase orderChoiceApple:\n\t\tworkflow.ExecuteActivity(ctx, orderAppleActivity, orderChoice)\n\tcase orderChoiceBanana:\n\t\tworkflow.ExecuteActivity(ctx, orderBananaActivity, orderChoice)\n\tcase orderChoiceCherry:\n\t\tworkflow.ExecuteActivity(ctx, orderCherryActivity, orderChoice)\n\tcase orderChoiceOrange:\n\t\tworkflow.ExecuteActivity(ctx, orderOrangeActivity, orderChoice)\n\tdefault:\n\t\tlogger.Error(\"Unexpected order\", zap.String(\"Choice\", orderChoice))\n\t}\n\n\tlogger.Info(\"Workflow completed.\")\n\treturn nil\n}\n\nfunc getOrderActivity() (string, error) {\n\tidx := rand.Intn(len(_orderChoices))\n\torder := _orderChoices[idx]\n\tfmt.Printf(\"Order is for %s\\n\", order)\n\treturn order, nil\n}\n\nfunc orderAppleActivity(choice string) error {\n\tfmt.Printf(\"Order choice: %v\\n\", choice)\n\treturn nil\n}\n\nfunc orderBananaActivity(choice string) error {\n\tfmt.Printf(\"Order choice: %v\\n\", choice)\n\treturn nil\n}\n\nfunc orderCherryActivity(choice string) error {\n\tfmt.Printf(\"Order choice: %v\\n\", choice)\n\treturn nil\n}\n\nfunc orderOrangeActivity(choice string) error {\n\tfmt.Printf(\"Order choice: %v\\n\", choice)\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/choice/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\n\t// Start Worker.\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflowMultiChoice(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"multi_choice_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, multiChoiceWorkflow)\n}\n\nfunc startWorkflowExclusiveChoice(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"single_choice_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, exclusiveChoiceWorkflow)\n}\n\nfunc main() {\n\tvar mode, sampleCase string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.StringVar(&sampleCase, \"c\", \"single\", \"Sample case to run.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(exclusiveChoiceWorkflow)\n\t\th.RegisterWorkflow(multiChoiceWorkflow)\n\t\th.RegisterActivity(getOrderActivity)\n\t\th.RegisterActivity(orderAppleActivity)\n\t\th.RegisterActivity(orderBananaActivity)\n\t\th.RegisterActivity(orderCherryActivity)\n\t\th.RegisterActivity(orderOrangeActivity)\n\t\th.RegisterActivity(getBasketOrderActivity)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tswitch sampleCase {\n\t\tcase \"multi\":\n\t\t\tstartWorkflowMultiChoice(&h)\n\t\tdefault:\n\t\t\tstartWorkflowExclusiveChoice(&h)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/choice/multi_choice_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"math/rand\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * This multi choice sample workflow Execute different parallel branches based on the result of an activity.\n */\n\n// multiChoiceWorkflow Workflow Decider.\nfunc multiChoiceWorkflow(ctx workflow.Context) error {\n\t// Get basket order.\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tvar choices []string\n\terr := workflow.ExecuteActivity(ctx, getBasketOrderActivity).Get(ctx, &choices)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlogger := workflow.GetLogger(ctx)\n\n\tvar futures []workflow.Future\n\tfor _, item := range choices {\n\t\t// choose next activity based on order result\n\t\tvar f workflow.Future\n\t\tswitch item {\n\t\tcase orderChoiceApple:\n\t\t\tf = workflow.ExecuteActivity(ctx, orderAppleActivity, item)\n\t\tcase orderChoiceBanana:\n\t\t\tf = workflow.ExecuteActivity(ctx, orderBananaActivity, item)\n\t\tcase orderChoiceCherry:\n\t\t\tf = workflow.ExecuteActivity(ctx, orderCherryActivity, item)\n\t\tcase orderChoiceOrange:\n\t\t\tf = workflow.ExecuteActivity(ctx, orderOrangeActivity, item)\n\t\tdefault:\n\t\t\tlogger.Error(\"Unexpected order.\", zap.String(\"Order\", item))\n\t\t\treturn errors.New(\"Invalid Choice\")\n\t\t}\n\t\tfutures = append(futures, f)\n\t}\n\n\t// wait until all items in the basket order are processed\n\tfor _, future := range futures {\n\t\tfuture.Get(ctx, nil)\n\t}\n\n\tlogger.Info(\"Workflow completed.\")\n\treturn nil\n}\n\nfunc getBasketOrderActivity(ctx context.Context) ([]string, error) {\n\tvar basket []string\n\tfor _, item := range _orderChoices {\n\t\t// some random decision\n\t\tif rand.Float32() <= 0.65 {\n\t\t\tbasket = append(basket, item)\n\t\t}\n\t}\n\n\tif len(basket) == 0 {\n\t\tbasket = append(basket, _orderChoices[rand.Intn(len(_orderChoices))])\n\t}\n\n\tactivity.GetLogger(ctx).Info(\"Get basket order.\", zap.Strings(\"Orders\", basket))\n\treturn basket, nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/choice/workflow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/suite\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\ntype UnitTestSuite struct {\n\tsuite.Suite\n\ttestsuite.WorkflowTestSuite\n\n\tenv *testsuite.TestWorkflowEnvironment\n}\n\nfunc TestUnitTestSuite(t *testing.T) {\n\tsuite.Run(t, new(UnitTestSuite))\n}\n\nfunc (s *UnitTestSuite) SetupTest() {\n\ts.env = s.NewTestWorkflowEnvironment()\n\ts.env.RegisterWorkflow(exclusiveChoiceWorkflow)\n\ts.env.RegisterWorkflow(multiChoiceWorkflow)\n\ts.env.RegisterActivity(getOrderActivity)\n\ts.env.RegisterActivity(orderAppleActivity)\n\ts.env.RegisterActivity(orderBananaActivity)\n\ts.env.RegisterActivity(orderCherryActivity)\n\ts.env.RegisterActivity(orderOrangeActivity)\n\ts.env.RegisterActivity(getBasketOrderActivity)\n}\n\nfunc (s *UnitTestSuite) TearDownTest() {\n\ts.env.AssertExpectations(s.T())\n}\n\nfunc (s *UnitTestSuite) Test_ExclusiveChoiceWorkflow() {\n\ts.env.ExecuteWorkflow(exclusiveChoiceWorkflow)\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n}\n\nfunc (s *UnitTestSuite) Test_MultiChoiceWorkflow() {\n\ts.env.ExecuteWorkflow(multiChoiceWorkflow)\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n}\n"
  },
  {
    "path": "cmd/samples/recipes/consistentquery/README.md",
    "content": "This sample workflow demos how to use consistent query API to get the current state of running workflow.\n\nquery_workflow.go shows how to setup a custom workflow query handler\nquery_workflow_test.go shows how to unit-test query functionality\n\nSteps to run this sample:\n1) You need a cadence service running. See details in cmd/samples/README.md\n2) Run the following command to start worker\n```\n./bin/query -m worker\n```\n3) Run the following command to trigger a workflow execution. You should see workflowID and runID print out on screen.\n```\n./bin/query -m trigger\n```\n\nIt will start a workflow and then using signal+consistent query to operate the workflow. \n"
  },
  {
    "path": "cmd/samples/recipes/consistentquery/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc main() {\n\tvar mode, workflowID, runID, queryType string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker, trigger or query.\")\n\tflag.StringVar(&workflowID, \"w\", \"\", \"WorkflowID\")\n\tflag.StringVar(&runID, \"r\", \"\", \"RunID\")\n\tflag.StringVar(&queryType, \"t\", \"__stack_trace\", \"QueryType\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(queryWorkflow)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\twfID := \"query_\" + uuid.New()\n\t\tworkflowOptions := client.StartWorkflowOptions{\n\t\t\tID:                              wfID,\n\t\t\tTaskList:                        ApplicationName,\n\t\t\tExecutionStartToCloseTimeout:    time.Hour * 10,\n\t\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t\t}\n\t\th.StartWorkflow(workflowOptions, queryWorkflow)\n\t\tresult := -1\n\t\th.ConsistentQueryWorkflow(&result, wfID, \"\", \"state\")\n\t\tfmt.Println(\"initial query result after started:\", result)\n\n\t\th.SignalWorkflow(wfID, \"increase\", nil)\n\t\th.ConsistentQueryWorkflow(&result, wfID, \"\", \"state\")\n\t\tfmt.Println(\"query after 1 increase:\", result)\n\n\t\th.SignalWorkflow(wfID, \"increase\", nil)\n\t\th.SignalWorkflow(wfID, \"increase\", nil)\n\t\th.SignalWorkflow(wfID, \"increase\", nil)\n\t\th.SignalWorkflow(wfID, \"increase\", nil)\n\t\th.ConsistentQueryWorkflow(&result, wfID, \"\", \"state\")\n\t\tfmt.Println(\"query after 1 +4 increase:\", result)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/consistentquery/query_workflow.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"queryGroup\"\n\n// queryWorkflow is an implementation of cadence workflow to demo how to setup query handler\nfunc queryWorkflow(ctx workflow.Context) error {\n\tqueryResult := 0\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"QueryWorkflow started\")\n\t// setup query handler for query type \"state\"\n\terr := workflow.SetQueryHandler(ctx, \"state\", func(input []byte) (int, error) {\n\t\treturn queryResult, nil\n\t})\n\tif err != nil {\n\t\tlogger.Info(\"SetQueryHandler failed: \" + err.Error())\n\t\treturn err\n\t}\n\n\tsignalChan := workflow.GetSignalChannel(ctx, \"increase\")\n\n\ts := workflow.NewSelector(ctx)\n\ts.AddReceive(signalChan, func(c workflow.Channel, more bool) {\n\t\tc.Receive(ctx, nil)\n\t\tqueryResult +=1\n\t\tworkflow.GetLogger(ctx).Info(\"Received signal!\", zap.String(\"signal\", \"increase\"))\n\t})\n\tworkflow.Go(ctx, func(ctx workflow.Context) {\n\t\tfor  {\n\t\t\ts.Select(ctx)\n\t\t}\n\t})\n\n\n\t// to simulate workflow been blocked on something, we wait for a timer\n\tworkflow.NewTimer(ctx, time.Minute*2).Get(ctx, nil)\n\tlogger.Info(\"Timer fired\")\n\n\tlogger.Info(\"QueryWorkflow completed\")\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/crossdomain/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"github.com/google/uuid\"\n\tapiv1 \"github.com/uber/cadence-idl/go/proto/api/v1\"\n\t\"go.uber.org/cadence/compatibility\"\n\t\"go.uber.org/yarpc\"\n\t\"go.uber.org/yarpc/transport/grpc\"\n\t\"go.uber.org/zap\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n)\n\nconst (\n\ttasklist0    = \"cross-domain-tl0\"\n\ttasklist1    = \"cross-domain-tl1\"\n\ttasklist2    = \"cross-domain-tl2\"\n\tdomain0      = \"domain0\"\n\tdomain1      = \"domain1\"\n\tdomain2      = \"domain2\"\n\tportCluster0 = \"127.0.0.1:7833\"\n\tportCluster1 = \"127.0.0.1:8833\"\n\tportCluster2 = \"127.0.0.1:9833\"\n)\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker, trigger.\")\n\tflag.Parse()\n\tlogger, err := zap.NewProduction()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tswitch mode {\n\tcase \"worker0\":\n\t\tsetupWorker(domain0, tasklist0, portCluster0, []interface{}{wf0}, []interface{}{})\n\t\tlogger.Info(\"workers running for cluster 0....\")\n\t\tselect {}\n\tcase \"worker1\":\n\t\tsetupWorker(domain1, tasklist1, portCluster1, []interface{}{wf1}, []interface{}{activity1})\n\t\tlogger.Info(\"workers running for cluster 1....\")\n\t\tselect {}\n\tcase \"worker2\":\n\t\tsetupWorker(domain2, tasklist2, portCluster2, []interface{}{wf2}, []interface{}{activity2})\n\t\tlogger.Info(\"workers running for cluster 1....\")\n\t\tselect {}\n\tcase \"start\":\n\t\tclient1 := setupClient(domain0, portCluster0)\n\t\tid := uuid.New().String()\n\t\tctx, close := context.WithTimeout(context.Background(), time.Second*30)\n\t\tdefer close()\n\n\t\tres, err := client1.StartWorkflow(ctx, client.StartWorkflowOptions{\n\t\t\tID:                           id,\n\t\t\tTaskList:                     tasklist0,\n\t\t\tExecutionStartToCloseTimeout: 30 * time.Second,\n\t\t}, wf0)\n\t\tif err != nil {\n\t\t\tlogger.Error(\"error starting workflow\", zap.Error(err))\n\t\t}\n\t\tlogger.Info(\"started workflow for domain0\", zap.String(\"wf-id\", id), zap.Any(\"start-wf\", res))\n\t}\n}\n\nfunc setupClient(domain string, hostport string) client.Client {\n\tdispatcher := yarpc.NewDispatcher(yarpc.Config{\n\t\tName: \"client\",\n\t\tOutbounds: yarpc.Outbounds{\n\t\t\t\"cadence-frontend\": {Unary: grpc.NewTransport().NewSingleOutbound(hostport)},\n\t\t},\n\t})\n\n\terr := dispatcher.Start()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tclientConfig := dispatcher.ClientConfig(\"cadence-frontend\")\n\n\tsvc := compatibility.NewThrift2ProtoAdapter(\n\t\tapiv1.NewDomainAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkflowAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkerAPIYARPCClient(clientConfig),\n\t\tapiv1.NewVisibilityAPIYARPCClient(clientConfig),\n\t)\n\n\treturn client.NewClient(\n\t\tsvc,\n\t\tdomain,\n\t\t&client.Options{\n\t\t\tFeatureFlags: client.FeatureFlags{\n\t\t\t\tWorkflowExecutionAlreadyCompletedErrorEnabled: true,\n\t\t\t},\n\t\t})\n}\nfunc setupWorker(domain string, tl string, hostport string, wfs []interface{}, activities []interface{}) {\n\tdispatcher := yarpc.NewDispatcher(yarpc.Config{\n\t\tName: \"client\",\n\t\tOutbounds: yarpc.Outbounds{\n\t\t\t\"cadence-frontend\": {Unary: grpc.NewTransport().NewSingleOutbound(hostport)},\n\t\t},\n\t})\n\n\tlogger, err := zap.NewProduction()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\terr = dispatcher.Start()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tworkerOptions := worker.Options{\n\t\tLogger: logger,\n\t\tFeatureFlags: client.FeatureFlags{\n\t\t\tWorkflowExecutionAlreadyCompletedErrorEnabled: true,\n\t\t},\n\t}\n\n\tclientConfig := dispatcher.ClientConfig(\"cadence-frontend\")\n\n\tsvc := compatibility.NewThrift2ProtoAdapter(\n\t\tapiv1.NewDomainAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkflowAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkerAPIYARPCClient(clientConfig),\n\t\tapiv1.NewVisibilityAPIYARPCClient(clientConfig),\n\t)\n\n\tworker := worker.New(svc, domain, tl, workerOptions)\n\tfor i := range wfs {\n\t\tworker.RegisterWorkflow(wfs[i])\n\t}\n\tfor i := range activities {\n\t\tworker.RegisterActivity(activities[i])\n\t}\n\terr = worker.Start()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/crossdomain/wf.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"github.com/google/uuid\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n\t\"time\"\n)\n\n// some sample data to be passed around\ntype Data struct {\n\tVal string\n}\n\nfunc wf0(ctx workflow.Context) error {\n\t// first try launching a child workflow in another cluster, active in another region\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"starting child workflow in domain1, cluster 1\")\n\tctx1 := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{\n\t\tDomain:                       domain1,\n\t\tWorkflowID:                   \"wf-domain-1-\" + uuid.New().String(),\n\t\tTaskList:                     tasklist1,\n\t\tExecutionStartToCloseTimeout: 1 * time.Minute,\n\t})\n\terr := workflow.ExecuteChildWorkflow(ctx1, wf1, Data{Val: \"test\"}).Get(ctx1, nil)\n\tif err != nil {\n\t\tlogger.Error(\"got error executing child workflow\", zap.Error(err))\n\t}\n\tlogger.Info(\"Cross-cluster cross-domain workflow completed\", zap.Any(\"return-value\", nil))\n\n\t// now try a workflow active in the same cluster\n\tctx2 := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{\n\t\tDomain:                       domain2,\n\t\tWorkflowID:                   \"wf-domain-2\" + uuid.New().String(),\n\t\tTaskList:                     tasklist2,\n\t\tExecutionStartToCloseTimeout: 1 * time.Minute,\n\t})\n\terr = workflow.ExecuteChildWorkflow(ctx2, wf2, Data{Val: \"test\"}).Get(ctx2, nil)\n\tif err != nil {\n\t\tlogger.Error(\"got error executing child workflow\", zap.Error(err))\n\t}\n\tlogger.Info(\"same-cluster cross-domain child-workflow completed.\")\n\treturn nil\n}\n\nfunc wf1(ctx workflow.Context, args Data) error {\n\tif args.Val != \"test\" {\n\t\tpanic(\"wf1 did not receive expected args\")\n\t}\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    4 * time.Minute,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"workflow wf1 starting activity.\")\n\terr := workflow.ExecuteActivity(ctx, activity1).Get(ctx, nil)\n\tif err != nil {\n\t\tlogger.Error(\"activity error\", zap.Error(err))\n\t}\n\tlogger.Info(\"workflow wf1 completed activity.\")\n\tlogger.Info(\"workflow wf1 completed.\")\n\treturn nil\n}\n\nfunc wf2(ctx workflow.Context, args Data) error {\n\tlogger := workflow.GetLogger(ctx)\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    4 * time.Minute,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\tif args.Val != \"test\" {\n\t\tpanic(\"wf1 did not receive expected args\")\n\t}\n\terr := workflow.ExecuteActivity(ctx, activity2).Get(ctx, nil)\n\tif err != nil {\n\t\tlogger.Error(\"activity error\", zap.Error(err))\n\t}\n\tlogger.Info(\"workflow wf1 completed.\")\n\treturn nil\n}\n\nfunc activity1(ctx context.Context) (string, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"activity 1 - running\")\n\treturn \"Hello - activity 1\", nil\n}\n\nfunc activity2(ctx context.Context) (string, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"activity 2 - running\")\n\treturn \"Hello - activity 2\", nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/ctxpropagation/README.md",
    "content": "This sample workflow demos context propagation through a workflow. Details about context propagation are\navailable [here](https://cadenceworkflow.io/docs/03_goclient/16_tracing).\n\nThe sample workflow initializes the client with a context propagator which propagates\nspecific information in the `context.Context` object across the workflow. The `context.Context` object is populated\nwith the information prior to calling `StartWorkflow`. The workflow demonstrates that the information is available\nin the workflow and any activities executed.\n\nSteps to run this sample:\n1) You need a cadence service running. See details in cmd/samples/README.md\n2) Run the following command multiple times on different console window. This is to simulate running workers on multiple different machines.\n```\n./bin/ctxpropagation -m worker\n```\n3) Run the following command to execute the context .\n```\n./bin/ctxpropagation -m trigger\n```\n\nYou should see prints showing the context information available in the workflow\nand activities.\n"
  },
  {
    "path": "cmd/samples/recipes/ctxpropagation/activities.go",
    "content": "package main\n\nimport (\n\t\"context\"\n)\n\nconst (\n\tsampleActivityName = \"sampleActivity\"\n)\n\nfunc sampleActivity(ctx context.Context) (*Values, error) {\n\tif val := ctx.Value(propagateKey); val != nil {\n\t\tvals := val.(Values)\n\t\treturn &vals, nil\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/ctxpropagation/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options. Setup a custom context propagator.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope:          h.WorkerMetricScope,\n\t\tLogger:                h.Logger,\n\t\tEnableLoggingInReplay: true,\n\t\tContextPropagators: []workflow.ContextPropagator{\n\t\t\tNewContextPropagator(),\n\t\t},\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"ctxprop_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\tctx := context.Background()\n\tctx = context.WithValue(ctx, propagateKey, &Values{Key: \"test\", Value: \"tested\"})\n\th.StartWorkflowWithCtx(ctx, workflowOptions, sampleCtxPropWorkflow)\n}\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\t// Setup two context propagators - one string and one custom context.\n\th.CtxPropagators = []workflow.ContextPropagator{\n\t\tNewContextPropagator(),\n\t}\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(sampleCtxPropWorkflow)\n\t\th.RegisterActivityWithAlias(sampleActivity, sampleActivityName)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/ctxpropagation/propagator.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\n\t\"go.uber.org/cadence/.gen/go/shared\"\n\t\"go.uber.org/cadence/workflow\"\n)\n\ntype (\n\t// contextKey is an unexported type used as key for items stored in the\n\t// Context object\n\tcontextKey struct{}\n\n\t// propagator implements the custom context propagator\n\tpropagator struct{}\n\n\t// Values is a struct holding values\n\tValues struct {\n\t\tKey   string `json:\"key\"`\n\t\tValue string `json:\"value\"`\n\t}\n)\n\n// propagateKey is the key used to store the value in the Context object\nvar propagateKey = contextKey{}\n\n// propagationKey is the key used by the propagator to pass values through the\n// cadence server headers\nconst propagationKey = \"_prop\"\n\n// NewContextPropagator returns a context propagator that propagates a set of\n// string key-value pairs across a workflow\nfunc NewContextPropagator() workflow.ContextPropagator {\n\treturn &propagator{}\n}\n\n// Inject injects values from context into headers for propagation\nfunc (s *propagator) Inject(ctx context.Context, writer workflow.HeaderWriter) error {\n\tvalue := ctx.Value(propagateKey)\n\tpayload, err := json.Marshal(value)\n\tif err != nil {\n\t\treturn err\n\t}\n\twriter.Set(propagationKey, payload)\n\treturn nil\n}\n\n// InjectFromWorkflow injects values from context into headers for propagation\nfunc (s *propagator) InjectFromWorkflow(ctx workflow.Context, writer workflow.HeaderWriter) error {\n\tvalue := ctx.Value(propagateKey)\n\tpayload, err := json.Marshal(value)\n\tif err != nil {\n\t\treturn err\n\t}\n\twriter.Set(propagationKey, payload)\n\treturn nil\n}\n\n// Extract extracts values from headers and puts them into context\nfunc (s *propagator) Extract(ctx context.Context, reader workflow.HeaderReader) (context.Context, error) {\n\tif err := reader.ForEachKey(func(key string, value []byte) error {\n\t\tif key == propagationKey {\n\t\t\tvar values Values\n\t\t\tif err := json.Unmarshal(value, &values); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tctx = context.WithValue(ctx, propagateKey, values)\n\t\t}\n\t\treturn nil\n\t}); err != nil {\n\t\treturn nil, err\n\t}\n\treturn ctx, nil\n}\n\n// ExtractToWorkflow extracts values from headers and puts them into context\nfunc (s *propagator) ExtractToWorkflow(ctx workflow.Context, reader workflow.HeaderReader) (workflow.Context, error) {\n\tif err := reader.ForEachKey(func(key string, value []byte) error {\n\t\tif key == propagationKey {\n\t\t\tvar values Values\n\t\t\tif err := json.Unmarshal(value, &values); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tctx = workflow.WithValue(ctx, propagateKey, values)\n\t\t}\n\t\treturn nil\n\t}); err != nil {\n\t\treturn nil, err\n\t}\n\treturn ctx, nil\n}\n\n// SetValuesInHeader places the Values container inside the header \nfunc SetValuesInHeader(values Values, header *shared.Header) error {\n\tpayload, err := json.Marshal(values)\n\tif err == nil {\n\t\theader.Fields[propagationKey] = payload\n\t} else {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n"
  },
  {
    "path": "cmd/samples/recipes/ctxpropagation/workflow.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"CtxPropagatorGroup\"\n\n// sampleCtxPropWorkflow workflow decider\nfunc sampleCtxPropWorkflow(ctx workflow.Context) (err error) {\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Second * 5,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 2, // such a short timeout to make sample fail over very fast\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tif val := ctx.Value(propagateKey); val != nil {\n\t\tvals := val.(Values)\n\t\tworkflow.GetLogger(ctx).Info(\"custom context propagated to workflow\", zap.String(vals.Key, vals.Value))\n\t}\n\n\tvar values Values\n\tif err = workflow.ExecuteActivity(ctx, sampleActivity).Get(ctx, &values); err != nil {\n\t\tworkflow.GetLogger(ctx).Error(\"Workflow failed.\", zap.Error(err))\n\t\treturn err\n\t}\n\tworkflow.GetLogger(ctx).Info(\"context propagated to activity\", zap.String(values.Key, values.Value))\n\tworkflow.GetLogger(ctx).Info(\"Workflow completed.\")\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/ctxpropagation/workflow_test.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/suite\"\n\t\"go.uber.org/cadence/.gen/go/shared\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/testsuite\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n)\n\ntype UnitTestSuite struct {\n\tsuite.Suite\n\ttestsuite.WorkflowTestSuite\n\n\tenv    *testsuite.TestWorkflowEnvironment\n\theader *shared.Header\n}\n\nfunc TestUnitTestSuite(t *testing.T) {\n\tsuite.Run(t, new(UnitTestSuite))\n}\n\nfunc (s *UnitTestSuite) SetupTest() {\n\t// Set Context Propagators and Headers, these will be shared across all Context objects in the test\n\tcontextPropagators := []workflow.ContextPropagator{NewContextPropagator()}\n\ts.header = &shared.Header{\n\t\tFields: make(map[string][]byte),\n\t}\n\ts.SetContextPropagators(contextPropagators)\n\ts.SetHeader(s.header)\n\n\tworkerOptions := worker.Options{\n\t\tContextPropagators: contextPropagators,\n\t}\n\n\ts.env = s.NewTestWorkflowEnvironment()\n\ts.env.RegisterWorkflow(sampleCtxPropWorkflow)\n\ts.env.RegisterActivityWithOptions(sampleActivity, activity.RegisterOptions{Name: \"sampleActivity\"})\n\ts.env.SetWorkerOptions(workerOptions)\n}\n\nfunc (s *UnitTestSuite) Test_CtxPropWorkflow() {\n\texpectedCall := []string{\n\t\t\"sampleActivity\",\n\t}\n\n\tvar activityCalled []string\n\tvalues := Values{Key: \"sampleKey\", Value: \"sampleValue\"}\n\t// Place the values to be propagated in the header\n\tSetValuesInHeader(values, s.header)\n\n\ts.env.SetOnActivityStartedListener(func(activityInfo *activity.Info, ctx context.Context, args encoded.Values) {\n\t\tactivityType := activityInfo.ActivityType.Name\n\t\tactivityCalled = append(activityCalled, activityType)\n\t\tif activityType != expectedCall[0] {\n\t\t\tpanic(\"unexpected activity call\")\n\t\t}\n\t\tactualValuesInCtx := ctx.Value(propagateKey).(Values)\n\t\tif actualValuesInCtx.Key != values.Key {\n\t\t\tpanic(\"there was a problem propagating Values, the key field doesn't match\")\n\t\t}\n\t\tif actualValuesInCtx.Value != values.Value {\n\t\t\tpanic(\"there was a problem propagating Values, the value field doesn't match\")\n\t\t}\n\t})\n\ts.env.ExecuteWorkflow(sampleCtxPropWorkflow)\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n\ts.Equal(expectedCall, activityCalled)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/delaystart/delaystart_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * This is the hello world workflow sample.\n */\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"delaystartGroup\"\n\nconst delayStartWorkflowName = \"delayStartWorkflow\"\n\n// helloWorkflow workflow decider\nfunc delayStartWorkflow(ctx workflow.Context, delayStart time.Duration) error {\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"delaystart workflow started after waiting for \" + delayStart.String())\n\tvar helloworldResult string\n\terr := workflow.ExecuteActivity(ctx, delayStartActivity, delayStart).Get(ctx, &helloworldResult)\n\tif err != nil {\n\t\tlogger.Error(\"Activity failed after waiting for \"+delayStart.String(), zap.Error(err))\n\t\treturn err\n\t}\n\n\t// Adding a new activity to the workflow will result in a non-determinstic change for the workflow\n\t// Please check https://cadenceworkflow.io/docs/go-client/workflow-versioning/ for more information\n\t//\n\t// Un-commenting the following code and the TestReplayWorkflowHistoryFromFile in replay_test.go\n\t// will fail due to the non-determinstic change\n\t//\n\t// If you have a completed workflow execution without the following code and run the\n\t// TestWorkflowShadowing in shadow_test.go or start the worker in shadow mode (using -m shadower)\n\t// those two shadowing check will also fail due to the non-deterministic change\n\t//\n\t// err := workflow.ExecuteActivity(ctx, helloWorldActivity, name).Get(ctx, &helloworldResult)\n\t// if err != nil {\n\t// \tlogger.Error(\"Activity failed.\", zap.Error(err))\n\t// \treturn err\n\t// }\n\n\tlogger.Info(\"Workflow completed.\", zap.String(\"Result\", helloworldResult))\n\n\treturn nil\n}\n\nfunc delayStartActivity(ctx context.Context, delayStart time.Duration) (string, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"delayStartActivity started after \" + delayStart.String())\n\treturn \"Activity started after \" + delayStart.String(), nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/delaystart/delaystart_workflow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\nfunc Test_Workflow(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\n\tenv := testSuite.NewTestWorkflowEnvironment()\n\tenv.RegisterWorkflow(delayStartWorkflow)\n\tenv.RegisterActivity(delayStartActivity)\n\n\tvar activityMessage string\n\tenv.SetOnActivityCompletedListener(func(activityInfo *activity.Info, result encoded.Value, err error) {\n\t\tresult.Get(&activityMessage)\n\t})\n\n\tdelayStart := 30 * time.Second\n\tenv.ExecuteWorkflow(delayStartWorkflow, delayStart)\n\n\trequire.True(t, env.IsWorkflowCompleted())\n\trequire.NoError(t, env.GetWorkflowError())\n\trequire.Equal(t, \"Activity started after \"+delayStart.String(), activityMessage)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/delaystart/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t\tFeatureFlags: client.FeatureFlags{\n\t\t\tWorkflowExecutionAlreadyCompletedErrorEnabled: true,\n\t\t},\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startShadower(h *common.SampleHelper) {\n\tworkerOptions := worker.Options{\n\t\tMetricsScope:       h.WorkerMetricScope,\n\t\tLogger:             h.Logger,\n\t\tEnableShadowWorker: true,\n\t\tShadowOptions: worker.ShadowOptions{\n\t\t\tWorkflowTypes:  []string{delayStartWorkflowName},\n\t\t\tWorkflowStatus: []string{\"Completed\"},\n\t\t\tExitCondition: worker.ShadowExitCondition{\n\t\t\t\tShadowCount: 10,\n\t\t\t},\n\t\t},\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tdelayStart := 30 * time.Second\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"delaystart_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t\tDelayStart:                      delayStart,\n\t}\n\th.StartWorkflow(workflowOptions, delayStartWorkflowName, delayStart)\n}\n\nfunc registerWorkflowAndActivity(\n\th *common.SampleHelper,\n) {\n\th.RegisterWorkflowWithAlias(delayStartWorkflow, delayStartWorkflowName)\n\th.RegisterActivity(delayStartActivity)\n}\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker, trigger or shadower.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\tregisterWorkflowAndActivity(&h)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"shadower\":\n\t\tregisterWorkflowAndActivity(&h)\n\t\tstartShadower(&h)\n\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/dynamic/dynamic_workflow.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * The purpose of this sample is to demonstrate invocation of workflows and activities using name rather than strongly\n * typed function.\n */\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"dynamicGroup\"\n\n// GreetingsWorkflowName name used when workflow function is registered during init.  We use the fully qualified name to function\nconst GreetingsWorkflowName = \"main.sampleGreetingsWorkflow\"\n\n// Activity names used when activity function is registered during init.  We use the fully qualified name to function\nconst getNameActivityName = \"main.getNameActivity\"\nconst getGreetingActivityName = \"main.getGreetingActivity\"\nconst sayGreetingActivityName = \"main.sayGreetingActivity\"\n\n// sampleGreetingsWorkflow Workflow Decider.\nfunc sampleGreetingsWorkflow(ctx workflow.Context) error {\n\t// Get Greeting.\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tlogger := workflow.GetLogger(ctx)\n\tvar greetResult string\n\terr := workflow.ExecuteActivity(ctx, getGreetingActivityName).Get(ctx, &greetResult)\n\tif err != nil {\n\t\tlogger.Error(\"Get greeting failed.\", zap.Error(err))\n\t\treturn err\n\t}\n\n\t// Get Name.\n\tvar nameResult string\n\terr = workflow.ExecuteActivity(ctx, getNameActivityName).Get(ctx, &nameResult)\n\tif err != nil {\n\t\tlogger.Error(\"Get name failed.\", zap.Error(err))\n\t\treturn err\n\t}\n\n\t// Say Greeting.\n\tvar sayResult string\n\terr = workflow.ExecuteActivity(ctx, sayGreetingActivityName, greetResult, nameResult).Get(ctx, &sayResult)\n\tif err != nil {\n\t\tlogger.Error(\"Marshalling failed with error.\", zap.Error(err))\n\t\treturn err\n\t}\n\n\tlogger.Info(\"Workflow completed.\", zap.String(\"Result\", sayResult))\n\treturn nil\n}\n\n// Get Name Activity.\nfunc getNameActivity() (string, error) {\n\treturn \"Cadence\", nil\n}\n\n// Get Greeting Activity.\nfunc getGreetingActivity() (string, error) {\n\treturn \"Hello\", nil\n}\n\n// Say Greeting Activity.\nfunc sayGreetingActivity(greeting string, name string) (string, error) {\n\tresult := fmt.Sprintf(\"Greeting: %s %s!\\n\", greeting, name)\n\treturn result, nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/dynamic/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"dynamic_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, GreetingsWorkflowName)\n}\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(sampleGreetingsWorkflow)\n\t\th.RegisterActivityWithAlias(getGreetingActivity, getGreetingActivityName)\n\t\th.RegisterActivityWithAlias(getNameActivity, getNameActivityName)\n\t\th.RegisterActivityWithAlias(sayGreetingActivity, sayGreetingActivityName)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/dynamic/workflow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\nfunc TestDynamicWorkflow(t *testing.T) {\n\ta := assert.New(t)\n\ts := testsuite.WorkflowTestSuite{}\n\tenv := s.NewTestWorkflowEnvironment()\n\tenv.RegisterWorkflow(sampleGreetingsWorkflow)\n\tenv.RegisterActivityWithOptions(getGreetingActivity, activity.RegisterOptions{\n\t\tName: getGreetingActivityName,\n\t})\n\tenv.RegisterActivityWithOptions(getNameActivity, activity.RegisterOptions{\n\t\tName: getNameActivityName,\n\t})\n\tenv.RegisterActivityWithOptions(sayGreetingActivity, activity.RegisterOptions{\n\t\tName: sayGreetingActivityName,\n\t})\n\n\tenv.OnActivity(getGreetingActivityName).Return(\"Greet\", nil).Times(1)\n\tenv.OnActivity(getNameActivityName).Return(\"Name\", nil).Times(1)\n\tenv.OnActivity(sayGreetingActivityName, \"Greet\", \"Name\").Return(\"Greet Name\", nil).Times(1)\n\n\tenv.ExecuteWorkflow(sampleGreetingsWorkflow)\n\n\ta.True(env.IsWorkflowCompleted())\n\ta.NoError(env.GetWorkflowError())\n\tenv.AssertExpectations(t)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/greetings/greetings.json",
    "content": "[\n  {\n    \"eventId\": 1,\n    \"timestamp\": 1678403666710299250,\n    \"eventType\": \"WorkflowExecutionStarted\",\n    \"version\": 0,\n    \"taskId\": 2097152,\n    \"workflowExecutionStartedEventAttributes\": {\n      \"workflowType\": {\n        \"name\": \"github.com/uber-common/cadence-samples/cmd/samples/recipes/greetings.sampleGreetingsWorkflow\"\n      },\n      \"taskList\": {\n        \"name\": \"greetingsGroup\"\n      },\n      \"executionStartToCloseTimeoutSeconds\": 60,\n      \"taskStartToCloseTimeoutSeconds\": 60,\n      \"continuedExecutionRunId\": \"\",\n      \"originalExecutionRunId\": \"b36e5edb-288e-4b98-9b66-2170238f8fc7\",\n      \"identity\": \"8979@agautam-NV709R969P@@00c72a14-c860-4283-b900-a9f732951789\",\n      \"firstExecutionRunId\": \"b36e5edb-288e-4b98-9b66-2170238f8fc7\",\n      \"attempt\": 0,\n      \"cronSchedule\": \"\",\n      \"firstDecisionTaskBackoffSeconds\": 0,\n      \"header\": {}\n    }\n  },\n  {\n    \"eventId\": 2,\n    \"timestamp\": 1678403666710498292,\n    \"eventType\": \"DecisionTaskScheduled\",\n    \"version\": 0,\n    \"taskId\": 2097153,\n    \"decisionTaskScheduledEventAttributes\": {\n      \"taskList\": {\n        \"name\": \"greetingsGroup\"\n      },\n      \"startToCloseTimeoutSeconds\": 60,\n      \"attempt\": 0\n    }\n  },\n  {\n    \"eventId\": 3,\n    \"timestamp\": 1678403666741684958,\n    \"eventType\": \"DecisionTaskStarted\",\n    \"version\": 0,\n    \"taskId\": 2097158,\n    \"decisionTaskStartedEventAttributes\": {\n      \"scheduledEventId\": 2,\n      \"identity\": \"8950@agautam-NV709R969P@greetingsGroup@4e847c85-7deb-45f3-96e6-2fbd3655a224\",\n      \"requestId\": \"fcaf3c19-b8a8-47c8-9373-1986cba56ae2\"\n    }\n  },\n  {\n    \"eventId\": 4,\n    \"timestamp\": 1678403666764246458,\n    \"eventType\": \"DecisionTaskCompleted\",\n    \"version\": 0,\n    \"taskId\": 2097161,\n    \"decisionTaskCompletedEventAttributes\": {\n      \"scheduledEventId\": 2,\n      \"startedEventId\": 3,\n      \"identity\": \"8950@agautam-NV709R969P@greetingsGroup@4e847c85-7deb-45f3-96e6-2fbd3655a224\",\n      \"binaryChecksum\": \"4c71e1c8cb815b51ff74bc0dbbb86cfa\"\n    }\n  },\n  {\n    \"eventId\": 5,\n    \"timestamp\": 1678403666764500583,\n    \"eventType\": \"ActivityTaskScheduled\",\n    \"version\": 0,\n    \"taskId\": 2097162,\n    \"activityTaskScheduledEventAttributes\": {\n      \"activityId\": \"0\",\n      \"activityType\": {\n        \"name\": \"main.getGreetingActivity\"\n      },\n      \"taskList\": {\n        \"name\": \"greetingsGroup\"\n      },\n      \"scheduleToCloseTimeoutSeconds\": 60,\n      \"scheduleToStartTimeoutSeconds\": 60,\n      \"startToCloseTimeoutSeconds\": 60,\n      \"heartbeatTimeoutSeconds\": 20,\n      \"decisionTaskCompletedEventId\": 4,\n      \"header\": {}\n    }\n  },\n  {\n    \"eventId\": 6,\n    \"timestamp\": 1678403666764575958,\n    \"eventType\": \"ActivityTaskStarted\",\n    \"version\": 0,\n    \"taskId\": 2097163,\n    \"activityTaskStartedEventAttributes\": {\n      \"scheduledEventId\": 5,\n      \"identity\": \"8950@agautam-NV709R969P@greetingsGroup@4e847c85-7deb-45f3-96e6-2fbd3655a224\",\n      \"requestId\": \"0f95e6e3-f170-4f6c-bd92-720d2673fe2d\",\n      \"attempt\": 0,\n      \"lastFailureReason\": \"\"\n    }\n  },\n  {\n    \"eventId\": 7,\n    \"timestamp\": 1678403666779516000,\n    \"eventType\": \"ActivityTaskCompleted\",\n    \"version\": 0,\n    \"taskId\": 2097166,\n    \"activityTaskCompletedEventAttributes\": {\n      \"result\": \"IkhlbGxvIgo=\",\n      \"scheduledEventId\": 5,\n      \"startedEventId\": 6,\n      \"identity\": \"8950@agautam-NV709R969P@greetingsGroup@4e847c85-7deb-45f3-96e6-2fbd3655a224\"\n    }\n  },\n  {\n    \"eventId\": 8,\n    \"timestamp\": 1678403666779571208,\n    \"eventType\": \"DecisionTaskScheduled\",\n    \"version\": 0,\n    \"taskId\": 2097168,\n    \"decisionTaskScheduledEventAttributes\": {\n      \"taskList\": {\n        \"name\": \"agautam-NV709R969P:053628af-d5cb-4a19-80b2-fd09b728e9d9\"\n      },\n      \"startToCloseTimeoutSeconds\": 60,\n      \"attempt\": 0\n    }\n  },\n  {\n    \"eventId\": 9,\n    \"timestamp\": 1678403666794335375,\n    \"eventType\": \"DecisionTaskStarted\",\n    \"version\": 0,\n    \"taskId\": 2097172,\n    \"decisionTaskStartedEventAttributes\": {\n      \"scheduledEventId\": 8,\n      \"identity\": \"8950@agautam-NV709R969P@greetingsGroup@4e847c85-7deb-45f3-96e6-2fbd3655a224\",\n      \"requestId\": \"87e9fac9-6aa6-4778-8b1e-4e7850545f6a\"\n    }\n  },\n  {\n    \"eventId\": 10,\n    \"timestamp\": 1678403666811876125,\n    \"eventType\": \"DecisionTaskCompleted\",\n    \"version\": 0,\n    \"taskId\": 2097175,\n    \"decisionTaskCompletedEventAttributes\": {\n      \"scheduledEventId\": 8,\n      \"startedEventId\": 9,\n      \"identity\": \"8950@agautam-NV709R969P@greetingsGroup@4e847c85-7deb-45f3-96e6-2fbd3655a224\",\n      \"binaryChecksum\": \"4c71e1c8cb815b51ff74bc0dbbb86cfa\"\n    }\n  },\n  {\n    \"eventId\": 11,\n    \"timestamp\": 1678403666812034417,\n    \"eventType\": \"ActivityTaskScheduled\",\n    \"version\": 0,\n    \"taskId\": 2097176,\n    \"activityTaskScheduledEventAttributes\": {\n      \"activityId\": \"1\",\n      \"activityType\": {\n        \"name\": \"main.getNameActivity\"\n      },\n      \"taskList\": {\n        \"name\": \"greetingsGroup\"\n      },\n      \"scheduleToCloseTimeoutSeconds\": 60,\n      \"scheduleToStartTimeoutSeconds\": 60,\n      \"startToCloseTimeoutSeconds\": 60,\n      \"heartbeatTimeoutSeconds\": 20,\n      \"decisionTaskCompletedEventId\": 10,\n      \"header\": {}\n    }\n  },\n  {\n    \"eventId\": 12,\n    \"timestamp\": 1678403666812095833,\n    \"eventType\": \"ActivityTaskStarted\",\n    \"version\": 0,\n    \"taskId\": 2097177,\n    \"activityTaskStartedEventAttributes\": {\n      \"scheduledEventId\": 11,\n      \"identity\": \"8950@agautam-NV709R969P@greetingsGroup@4e847c85-7deb-45f3-96e6-2fbd3655a224\",\n      \"requestId\": \"8f2bf260-1cfa-4531-b054-cc8cacbe7b06\",\n      \"attempt\": 0,\n      \"lastFailureReason\": \"\"\n    }\n  },\n  {\n    \"eventId\": 13,\n    \"timestamp\": 1678403666823607458,\n    \"eventType\": \"ActivityTaskCompleted\",\n    \"version\": 0,\n    \"taskId\": 2097180,\n    \"activityTaskCompletedEventAttributes\": {\n      \"result\": \"IkNhZGVuY2UiCg==\",\n      \"scheduledEventId\": 11,\n      \"startedEventId\": 12,\n      \"identity\": \"8950@agautam-NV709R969P@greetingsGroup@4e847c85-7deb-45f3-96e6-2fbd3655a224\"\n    }\n  },\n  {\n    \"eventId\": 14,\n    \"timestamp\": 1678403666823669292,\n    \"eventType\": \"DecisionTaskScheduled\",\n    \"version\": 0,\n    \"taskId\": 2097182,\n    \"decisionTaskScheduledEventAttributes\": {\n      \"taskList\": {\n        \"name\": \"agautam-NV709R969P:053628af-d5cb-4a19-80b2-fd09b728e9d9\"\n      },\n      \"startToCloseTimeoutSeconds\": 60,\n      \"attempt\": 0\n    }\n  },\n  {\n    \"eventId\": 15,\n    \"timestamp\": 1678403666837054083,\n    \"eventType\": \"DecisionTaskStarted\",\n    \"version\": 0,\n    \"taskId\": 2097186,\n    \"decisionTaskStartedEventAttributes\": {\n      \"scheduledEventId\": 14,\n      \"identity\": \"8950@agautam-NV709R969P@greetingsGroup@4e847c85-7deb-45f3-96e6-2fbd3655a224\",\n      \"requestId\": \"519ec075-5402-4c58-b19d-0eddc15be712\"\n    }\n  },\n  {\n    \"eventId\": 16,\n    \"timestamp\": 1678403666851767917,\n    \"eventType\": \"DecisionTaskCompleted\",\n    \"version\": 0,\n    \"taskId\": 2097189,\n    \"decisionTaskCompletedEventAttributes\": {\n      \"scheduledEventId\": 14,\n      \"startedEventId\": 15,\n      \"identity\": \"8950@agautam-NV709R969P@greetingsGroup@4e847c85-7deb-45f3-96e6-2fbd3655a224\",\n      \"binaryChecksum\": \"4c71e1c8cb815b51ff74bc0dbbb86cfa\"\n    }\n  },\n  {\n    \"eventId\": 17,\n    \"timestamp\": 1678403666851909542,\n    \"eventType\": \"ActivityTaskScheduled\",\n    \"version\": 0,\n    \"taskId\": 2097190,\n    \"activityTaskScheduledEventAttributes\": {\n      \"activityId\": \"2\",\n      \"activityType\": {\n        \"name\": \"main.sayGreetingActivity\"\n      },\n      \"taskList\": {\n        \"name\": \"greetingsGroup\"\n      },\n      \"input\": \"IkhlbGxvIgoiQ2FkZW5jZSIK\",\n      \"scheduleToCloseTimeoutSeconds\": 60,\n      \"scheduleToStartTimeoutSeconds\": 60,\n      \"startToCloseTimeoutSeconds\": 60,\n      \"heartbeatTimeoutSeconds\": 20,\n      \"decisionTaskCompletedEventId\": 16,\n      \"header\": {}\n    }\n  },\n  {\n    \"eventId\": 18,\n    \"timestamp\": 1678403666851975417,\n    \"eventType\": \"ActivityTaskStarted\",\n    \"version\": 0,\n    \"taskId\": 2097191,\n    \"activityTaskStartedEventAttributes\": {\n      \"scheduledEventId\": 17,\n      \"identity\": \"8950@agautam-NV709R969P@greetingsGroup@4e847c85-7deb-45f3-96e6-2fbd3655a224\",\n      \"requestId\": \"8f8d541c-96fe-4c96-9af7-70a70a9be096\",\n      \"attempt\": 0,\n      \"lastFailureReason\": \"\"\n    }\n  },\n  {\n    \"eventId\": 19,\n    \"timestamp\": 1678403666864198625,\n    \"eventType\": \"ActivityTaskCompleted\",\n    \"version\": 0,\n    \"taskId\": 2097194,\n    \"activityTaskCompletedEventAttributes\": {\n      \"result\": \"IkdyZWV0aW5nOiBIZWxsbyBDYWRlbmNlIVxuIgo=\",\n      \"scheduledEventId\": 17,\n      \"startedEventId\": 18,\n      \"identity\": \"8950@agautam-NV709R969P@greetingsGroup@4e847c85-7deb-45f3-96e6-2fbd3655a224\"\n    }\n  },\n  {\n    \"eventId\": 20,\n    \"timestamp\": 1678403666864249708,\n    \"eventType\": \"DecisionTaskScheduled\",\n    \"version\": 0,\n    \"taskId\": 2097196,\n    \"decisionTaskScheduledEventAttributes\": {\n      \"taskList\": {\n        \"name\": \"agautam-NV709R969P:053628af-d5cb-4a19-80b2-fd09b728e9d9\"\n      },\n      \"startToCloseTimeoutSeconds\": 60,\n      \"attempt\": 0\n    }\n  },\n  {\n    \"eventId\": 21,\n    \"timestamp\": 1678403666877005167,\n    \"eventType\": \"DecisionTaskStarted\",\n    \"version\": 0,\n    \"taskId\": 2097200,\n    \"decisionTaskStartedEventAttributes\": {\n      \"scheduledEventId\": 20,\n      \"identity\": \"8950@agautam-NV709R969P@greetingsGroup@4e847c85-7deb-45f3-96e6-2fbd3655a224\",\n      \"requestId\": \"919b570f-0ebf-4b88-94dd-cc183c898ad5\"\n    }\n  },\n  {\n    \"eventId\": 22,\n    \"timestamp\": 1678403666891816208,\n    \"eventType\": \"DecisionTaskCompleted\",\n    \"version\": 0,\n    \"taskId\": 2097203,\n    \"decisionTaskCompletedEventAttributes\": {\n      \"scheduledEventId\": 20,\n      \"startedEventId\": 21,\n      \"identity\": \"8950@agautam-NV709R969P@greetingsGroup@4e847c85-7deb-45f3-96e6-2fbd3655a224\",\n      \"binaryChecksum\": \"4c71e1c8cb815b51ff74bc0dbbb86cfa\"\n    }\n  },\n  {\n    \"eventId\": 23,\n    \"timestamp\": 1678403666891938750,\n    \"eventType\": \"WorkflowExecutionCompleted\",\n    \"version\": 0,\n    \"taskId\": 2097204,\n    \"workflowExecutionCompletedEventAttributes\": {\n      \"decisionTaskCompletedEventId\": 22\n    }\n  }\n]"
  },
  {
    "path": "cmd/samples/recipes/greetings/greetings_workflow.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * This greetings sample workflow executes 3 activities in sequential. It gets greeting and name from 2 different activities,\n * and then pass greeting and name as input to a 3rd activity to generate final greetings.\n */\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"greetingsGroup\"\n\n// sampleGreetingsWorkflow Workflow Decider.\nfunc sampleGreetingsWorkflow(ctx workflow.Context) error {\n\t// Get Greeting.\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tlogger := workflow.GetLogger(ctx)\n\tvar greetResult string\n\terr := workflow.ExecuteActivity(ctx, getGreetingActivity).Get(ctx, &greetResult)\n\tif err != nil {\n\t\tlogger.Error(\"Get greeting failed.\", zap.Error(err))\n\t\treturn err\n\t}\n\n\t// Get Name.\n\tvar nameResult string\n\terr = workflow.ExecuteActivity(ctx, getNameActivity).Get(ctx, &nameResult)\n\tif err != nil {\n\t\tlogger.Error(\"Get name failed.\", zap.Error(err))\n\t\treturn err\n\t}\n\n\t// Say Greeting.\n\tvar sayResult string\n\terr = workflow.ExecuteActivity(ctx, sayGreetingActivity, greetResult, nameResult).Get(ctx, &sayResult)\n\tif err != nil {\n\t\tlogger.Error(\"Marshalling failed with error.\", zap.Error(err))\n\t\treturn err\n\t}\n\n\tlogger.Info(\"Workflow completed.\", zap.String(\"Result\", sayResult))\n\treturn nil\n}\n\n// Get Name Activity.\nfunc getNameActivity() (string, error) {\n\treturn \"Cadence\", nil\n}\n\n// Get Greeting Activity.\nfunc getGreetingActivity() (string, error) {\n\treturn \"Hello\", nil\n}\n\n// Say Greeting Activity.\nfunc sayGreetingActivity(greeting string, name string) (string, error) {\n\tresult := fmt.Sprintf(\"Greeting: %s %s!\\n\", greeting, name)\n\treturn result, nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/greetings/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"greetings_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, sampleGreetingsWorkflow)\n}\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(sampleGreetingsWorkflow)\n\t\th.RegisterActivity(getGreetingActivity)\n\t\th.RegisterActivity(getNameActivity)\n\t\th.RegisterActivity(sayGreetingActivity)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/greetings/replay_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/zap/zaptest\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\n// This replay test is the recommended way to make sure changing workflow code is backward compatible without non-deterministic errors.\n// \"greetings.json\" can be downloaded from cadence CLI:\n//\n//\tcadence --do default wf show -w greetings_5d5f8e5c-4807-444d-9dc5-80abea22a324 --output_filename ~/tmp/greetings.json\n//\n// Or from Cadence Web UI. And you may need to change workflowType in the first event.\nfunc TestReplayWorkflowHistoryFromFile(t *testing.T) {\n\treplayer := worker.NewWorkflowReplayer()\n\n\treplayer.RegisterWorkflow(sampleGreetingsWorkflow)\n\n\terr := replayer.ReplayWorkflowHistoryFromJSONFile(zaptest.NewLogger(t), \"greetings.json\")\n\trequire.NoError(t, err)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/greetings/shadow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap/zaptest\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\nconst (\n\tconfigFile = \"../../../../config/development.yaml\"\n)\n\n// Make sure cadence-server is running at the address specified in the\n// config/development.yaml file before running this test\nfunc TestWorkflowShadowing(t *testing.T) {\n\tt.Skip(\"need connection to cadence server\")\n\n\tshadowOptions := worker.ShadowOptions{\n\t\tWorkflowTypes:  []string{\"sampleGreetingsWorkflow\"},\n\t\tWorkflowStatus: []string{\"Completed\"},\n\t\tWorkflowStartTimeFilter: worker.TimeFilter{\n\t\t\tMinTimestamp: time.Now().Add(-time.Hour),\n\t\t},\n\t}\n\n\tvar helper common.SampleHelper\n\thelper.SetConfigFile(configFile)\n\thelper.SetupServiceConfig()\n\tservice, err := helper.Builder.BuildServiceClient()\n\trequire.NoError(t, err)\n\n\tshadower, err := worker.NewWorkflowShadower(service, helper.Config.DomainName, shadowOptions, worker.ReplayOptions{}, zaptest.NewLogger(t))\n\trequire.NoError(t, err)\n\n\tshadower.RegisterWorkflowWithOptions(sampleGreetingsWorkflow, workflow.RegisterOptions{Name: \"sampleGreetingsWorkflow\"})\n\n\terr = shadower.Run()\n\trequire.NoError(t, err)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/greetings/workflow_test.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/suite\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\ntype UnitTestSuite struct {\n\tsuite.Suite\n\ttestsuite.WorkflowTestSuite\n\n\tenv *testsuite.TestWorkflowEnvironment\n}\n\nfunc TestUnitTestSuite(t *testing.T) {\n\tsuite.Run(t, new(UnitTestSuite))\n}\n\nfunc (s *UnitTestSuite) SetupTest() {\n\ts.env = s.NewTestWorkflowEnvironment()\n\ts.env.RegisterWorkflow(sampleGreetingsWorkflow)\n\ts.env.RegisterActivity(getGreetingActivity)\n\ts.env.RegisterActivity(getNameActivity)\n\ts.env.RegisterActivity(sayGreetingActivity)\n}\n\nfunc (s *UnitTestSuite) TearDownTest() {\n\ts.env.AssertExpectations(s.T())\n}\n\nfunc (s *UnitTestSuite) Test_SampleGreetingsWorkflow() {\n\tsayGreetingActivityName := \"github.com/uber-common/cadence-samples/cmd/samples/recipes/greetings.sayGreetingActivity\"\n\tvar startCalled, endCalled bool\n\ts.env.SetOnActivityStartedListener(func(activityInfo *activity.Info, ctx context.Context, args encoded.Values) {\n\t\tif sayGreetingActivityName == activityInfo.ActivityType.Name {\n\t\t\tvar greeting, name string\n\t\t\targs.Get(&greeting, &name)\n\t\t\ts.Equal(\"Hello\", greeting)\n\t\t\ts.Equal(\"Cadence\", name)\n\t\t\tstartCalled = true\n\t\t}\n\t})\n\ts.env.SetOnActivityCompletedListener(func(activityInfo *activity.Info, result encoded.Value, err error) {\n\t\tif sayGreetingActivityName == activityInfo.ActivityType.Name {\n\t\t\tvar sayResult string\n\t\t\tresult.Get(&sayResult)\n\t\t\ts.Equal(\"Greeting: Hello Cadence!\\n\", sayResult)\n\t\t\tendCalled = true\n\t\t}\n\t})\n\n\ts.env.ExecuteWorkflow(sampleGreetingsWorkflow)\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n\ts.True(startCalled)\n\ts.True(endCalled)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/helloworld/activity_logger_test.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/testsuite\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n\t\"testing\"\n)\n\nfunc sampleActivity(ctx context.Context) error {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"test logging\")\n\treturn nil\n}\n\nfunc Test_Activity_Noop_Logger(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\tenv := testSuite.NewTestActivityEnvironment()\n\tenv.RegisterActivity(sampleActivity)\n\tval, err := env.ExecuteActivity(sampleActivity)\n\trequire.Nil(t, err)\n\trequire.True(t, !val.HasValue())\n}\n\nfunc Test_Activity_Print_Logger(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\tenv := testSuite.NewTestActivityEnvironment()\n\n\tlogger, err := zap.NewProduction()\n\trequire.Nil(t, err)\n\n\tvar outputLogs []string\n\tlogger = logger.WithOptions(zap.Hooks(\n\t\tfunc(entry zapcore.Entry) error {\n\t\t\toutputLogs = append(outputLogs, entry.Message)\n\t\t\treturn nil\n\t\t},\n\t))\n\n\tenv.SetWorkerOptions(worker.Options{\n\t\tLogger: logger,\n\t})\n\tenv.RegisterActivity(sampleActivity)\n\n\tval, err := env.ExecuteActivity(sampleActivity)\n\n\trequire.Nil(t, err)\n\trequire.True(t, !val.HasValue())\n\trequire.True(t, len(outputLogs)==1)\n\trequire.True(t, outputLogs[0] == \"test logging\")\n}"
  },
  {
    "path": "cmd/samples/recipes/helloworld/helloworld.json",
    "content": "[\n  {\n    \"eventId\":1,\n    \"timestamp\":1558126752505445000,\n    \"eventType\":\"WorkflowExecutionStarted\",\n    \"version\":-24,\n    \"taskId\":33554432,\n    \"workflowExecutionStartedEventAttributes\":{\n      \"workflowType\":{\n        \"name\":\"github.com/uber-common/cadence-samples/cmd/samples/recipes/helloworld.helloWorldWorkflow\"\n      },\n      \"taskList\":{\n        \"name\":\"helloWorldGroup\"\n      },\n      \"input\":\"IkNhZGVuY2UiCg==\",\n      \"executionStartToCloseTimeoutSeconds\":3600,\n      \"taskStartToCloseTimeoutSeconds\":10,\n      \"identity\":\"66434@longer-C02V60N3HTDG@\",\n      \"attempt\":0,\n      \"firstDecisionTaskBackoffSeconds\":0\n    }\n  },\n  {\n    \"eventId\":2,\n    \"timestamp\":1558126752505631000,\n    \"eventType\":\"DecisionTaskScheduled\",\n    \"version\":-24,\n    \"taskId\":33554433,\n    \"decisionTaskScheduledEventAttributes\":{\n      \"taskList\":{\n        \"name\":\"helloWorldGroup\"\n      },\n      \"startToCloseTimeoutSeconds\":10,\n      \"attempt\":0\n    }\n  },\n  {\n    \"eventId\":3,\n    \"timestamp\":1558126757360699000,\n    \"eventType\":\"DecisionTaskStarted\",\n    \"version\":-24,\n    \"taskId\":33554438,\n    \"decisionTaskStartedEventAttributes\":{\n      \"scheduledEventId\":2,\n      \"identity\":\"66471@longer-C02V60N3HTDG@helloWorldGroup\",\n      \"requestId\":\"665325ec-74eb-4337-bf82-fceeb60e2196\"\n    }\n  },\n  {\n    \"eventId\":4,\n    \"timestamp\":1558126757385307000,\n    \"eventType\":\"DecisionTaskCompleted\",\n    \"version\":-24,\n    \"taskId\":33554441,\n    \"decisionTaskCompletedEventAttributes\":{\n      \"scheduledEventId\":2,\n      \"startedEventId\":3,\n      \"identity\":\"66471@longer-C02V60N3HTDG@helloWorldGroup\",\n      \"binaryChecksum\":\"b2e32759177ccbb3e67ad7694aec233c\"\n    }\n  },\n  {\n    \"eventId\":5,\n    \"timestamp\":1558126757385333000,\n    \"eventType\":\"ActivityTaskScheduled\",\n    \"version\":-24,\n    \"taskId\":33554442,\n    \"activityTaskScheduledEventAttributes\":{\n      \"activityId\":\"0\",\n      \"activityType\":{\n        \"name\":\"github.com/uber-common/cadence-samples/cmd/samples/recipes/helloworld.helloWorldActivity\"\n      },\n      \"taskList\":{\n        \"name\":\"helloWorldGroup\"\n      },\n      \"input\":\"IkNhZGVuY2UiCg==\",\n      \"scheduleToCloseTimeoutSeconds\":3600,\n      \"scheduleToStartTimeoutSeconds\":3600,\n      \"startToCloseTimeoutSeconds\":3600,\n      \"heartbeatTimeoutSeconds\":3600,\n      \"decisionTaskCompletedEventId\":4\n    }\n  },\n  {\n    \"eventId\":6,\n    \"timestamp\":1558126757393919000,\n    \"eventType\":\"ActivityTaskStarted\",\n    \"version\":-24,\n    \"taskId\":33554446,\n    \"activityTaskStartedEventAttributes\":{\n      \"scheduledEventId\":5,\n      \"identity\":\"66471@longer-C02V60N3HTDG@helloWorldGroup\",\n      \"requestId\":\"45c4006a-ae7c-4392-baa6-c090857f884b\",\n      \"attempt\":0\n    }\n  },\n  {\n    \"eventId\":7,\n    \"timestamp\":1558126757403468000,\n    \"eventType\":\"ActivityTaskCompleted\",\n    \"version\":-24,\n    \"taskId\":33554447,\n    \"activityTaskCompletedEventAttributes\":{\n      \"result\":\"IkhlbGxvIENhZGVuY2UhIgo=\",\n      \"scheduledEventId\":5,\n      \"startedEventId\":6,\n      \"identity\":\"66471@longer-C02V60N3HTDG@helloWorldGroup\"\n    }\n  },\n  {\n    \"eventId\":8,\n    \"timestamp\":1558126757403476000,\n    \"eventType\":\"DecisionTaskScheduled\",\n    \"version\":-24,\n    \"taskId\":33554450,\n    \"decisionTaskScheduledEventAttributes\":{\n      \"taskList\":{\n        \"name\":\"longer-C02V60N3HTDG:33ab3ada-4636-4386-8575-81dd8dc02e9a\"\n      },\n      \"startToCloseTimeoutSeconds\":10,\n      \"attempt\":0\n    }\n  },\n  {\n    \"eventId\":9,\n    \"timestamp\":1558126757410564000,\n    \"eventType\":\"DecisionTaskStarted\",\n    \"version\":-24,\n    \"taskId\":33554454,\n    \"decisionTaskStartedEventAttributes\":{\n      \"scheduledEventId\":8,\n      \"identity\":\"66471@longer-C02V60N3HTDG@helloWorldGroup\",\n      \"requestId\":\"cb1fdadf-f46b-4840-9b97-863f4b3b6b11\"\n    }\n  },\n  {\n    \"eventId\":10,\n    \"timestamp\":1558126757491491000,\n    \"eventType\":\"DecisionTaskCompleted\",\n    \"version\":-24,\n    \"taskId\":33554457,\n    \"decisionTaskCompletedEventAttributes\":{\n      \"scheduledEventId\":8,\n      \"startedEventId\":9,\n      \"identity\":\"66471@longer-C02V60N3HTDG@helloWorldGroup\",\n      \"binaryChecksum\":\"b2e32759177ccbb3e67ad7694aec233c\"\n    }\n  },\n  {\n    \"eventId\":11,\n    \"timestamp\":1558126757491513000,\n    \"eventType\":\"WorkflowExecutionCompleted\",\n    \"version\":-24,\n    \"taskId\":33554458,\n    \"workflowExecutionCompletedEventAttributes\":{\n      \"decisionTaskCompletedEventId\":10\n    }\n  }\n]"
  },
  {
    "path": "cmd/samples/recipes/helloworld/helloworld_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * This is the hello world workflow sample.\n */\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"helloWorldGroup\"\n\nconst helloWorldWorkflowName = \"helloWorldWorkflow\"\n\n// helloWorkflow workflow decider\nfunc helloWorldWorkflow(ctx workflow.Context, name string) error {\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"helloworld workflow started\")\n\tvar helloworldResult string\n\terr := workflow.ExecuteActivity(ctx, helloWorldActivity, name).Get(ctx, &helloworldResult)\n\tif err != nil {\n\t\tlogger.Error(\"Activity failed.\", zap.Error(err))\n\t\treturn err\n\t}\n\n\t// Adding a new activity to the workflow will result in a non-determinstic change for the workflow\n\t// Please check https://cadenceworkflow.io/docs/go-client/workflow-versioning/ for more information\n\t//\n\t// Un-commenting the following code and the TestReplayWorkflowHistoryFromFile in replay_test.go\n\t// will fail due to the non-determinstic change\n\t//\n\t// If you have a completed workflow execution without the following code and run the\n\t// TestWorkflowShadowing in shadow_test.go or start the worker in shadow mode (using -m shadower)\n\t// those two shadowing check will also fail due to the non-deterministic change\n\t//\n\t// err := workflow.ExecuteActivity(ctx, helloWorldActivity, name).Get(ctx, &helloworldResult)\n\t// if err != nil {\n\t// \tlogger.Error(\"Activity failed.\", zap.Error(err))\n\t// \treturn err\n\t// }\n\n\tlogger.Info(\"Workflow completed.\", zap.String(\"Result\", helloworldResult))\n\n\treturn nil\n}\n\nfunc helloWorldActivity(ctx context.Context, name string) (string, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"helloworld activity started\")\n\treturn \"Hello \" + name + \"!\", nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/helloworld/helloworld_workflow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\nfunc Test_Workflow(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\n\tenv := testSuite.NewTestWorkflowEnvironment()\n\tenv.RegisterWorkflow(helloWorldWorkflow)\n\tenv.RegisterActivity(helloWorldActivity)\n\n\tvar activityMessage string\n\tenv.SetOnActivityCompletedListener(func(activityInfo *activity.Info, result encoded.Value, err error) {\n\t\tresult.Get(&activityMessage)\n\t})\n\n\tenv.ExecuteWorkflow(helloWorldWorkflow, \"world\")\n\n\trequire.True(t, env.IsWorkflowCompleted())\n\trequire.NoError(t, env.GetWorkflowError())\n\trequire.Equal(t, \"Hello world!\", activityMessage)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/helloworld/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t\tFeatureFlags: client.FeatureFlags{\n\t\t\tWorkflowExecutionAlreadyCompletedErrorEnabled: true,\n\t\t},\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startShadower(h *common.SampleHelper) {\n\tworkerOptions := worker.Options{\n\t\tMetricsScope:       h.WorkerMetricScope,\n\t\tLogger:             h.Logger,\n\t\tEnableShadowWorker: true,\n\t\tShadowOptions: worker.ShadowOptions{\n\t\t\tWorkflowTypes:  []string{helloWorldWorkflowName},\n\t\t\tWorkflowStatus: []string{\"Completed\"},\n\t\t\tExitCondition: worker.ShadowExitCondition{\n\t\t\t\tShadowCount: 10,\n\t\t\t},\n\t\t},\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"helloworld_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, helloWorldWorkflowName, \"Cadence\")\n}\n\nfunc registerWorkflowAndActivity(\n\th *common.SampleHelper,\n) {\n\th.RegisterWorkflowWithAlias(helloWorldWorkflow, helloWorldWorkflowName)\n\th.RegisterActivity(helloWorldActivity)\n}\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker, trigger or shadower.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\tregisterWorkflowAndActivity(&h)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"shadower\":\n\t\tregisterWorkflowAndActivity(&h)\n\t\tstartShadower(&h)\n\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/helloworld/replay_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/zap/zaptest\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\n// This replay test is the recommended way to make sure changing workflow code is backward compatible without non-deterministic errors.\n// \"helloworld.json\" can be downloaded from cadence CLI:\n//\n//\tcadence --do default wf show -w helloworld_d002cd3a-aeee-4a11-aa30-1c62385b4d87 --output_filename ~/tmp/helloworld.json\n//\n// Or from Cadence Web UI. And you may need to change workflowType in the first event.\nfunc TestReplayWorkflowHistoryFromFile(t *testing.T) {\n\treplayer := worker.NewWorkflowReplayer()\n\n\treplayer.RegisterWorkflow(helloWorldWorkflow)\n\n\terr := replayer.ReplayWorkflowHistoryFromJSONFile(zaptest.NewLogger(t), \"helloworld.json\")\n\trequire.NoError(t, err)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/helloworld/shadow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap/zaptest\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\nconst (\n\tconfigFile = \"../../../../config/development.yaml\"\n)\n\n// Make sure cadence-server is running at the address specified in the\n// config/development.yaml file before running this test\nfunc TestWorkflowShadowing(t *testing.T) {\n\tt.Skip(\"need connection to cadence server\")\n\n\tshadowOptions := worker.ShadowOptions{\n\t\tWorkflowTypes:  []string{helloWorldWorkflowName},\n\t\tWorkflowStatus: []string{\"Completed\"},\n\t\tWorkflowStartTimeFilter: worker.TimeFilter{\n\t\t\tMinTimestamp: time.Now().Add(-time.Hour),\n\t\t},\n\t}\n\n\tvar helper common.SampleHelper\n\thelper.SetConfigFile(configFile)\n\thelper.SetupServiceConfig()\n\tservice, err := helper.Builder.BuildServiceClient()\n\trequire.NoError(t, err)\n\n\tshadower, err := worker.NewWorkflowShadower(service, helper.Config.DomainName, shadowOptions, worker.ReplayOptions{}, zaptest.NewLogger(t))\n\trequire.NoError(t, err)\n\n\tshadower.RegisterWorkflowWithOptions(helloWorldWorkflow, workflow.RegisterOptions{Name: helloWorldWorkflowName})\n\n\terr = shadower.Run()\n\trequire.NoError(t, err)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/localactivity/README.md",
    "content": "This sample workflow demos how to use local activity to execute short/quick operations efficiently.\n\nlocal_activity_workflow.go shows how to use local activity\nlocal_activity_workflow_test.go shows how to unit-test workflow with local activity\n\nSteps to run this sample:\n1) You need a cadence service running. See details in cmd/samples/README.md\n2) Run the following command to start worker\n```\n./bin/localactivity -m worker\n```\n3) Run the following command to trigger a workflow execution. You should see workflowID and runID print out on screen.\n```\n./bin/localactivity -m trigger\n```\n4) Run the following command to send signal \"_1_\" to the running workflow. You should see output that indicate 5 local activity has been run to check the conditions and one condition will be true which result in one activity to be scheduled.\n```\n./bin/localactivity -m signal -s _1_ -w <workflow ID from step 3>\n```\n5) Repeat step 4, but with different signal data, for example, send signal like _2_4_ to make 2 conditions true.\n```\n./bin/localactivity -m signal -s _2_4_ -w <workflow ID from step 3>\n```\n6) Run the following command this will exit the workflow.\n```\n./bin/localactivity -m signal -s exit\n```"
  },
  {
    "path": "cmd/samples/recipes/localactivity/local_activity_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.uber.org/cadence\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * Sample workflow that uses local activities.\n */\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"localActivityGroup\"\n\n// SignalName is the signal name that workflow is waiting for\nconst SignalName = \"trigger-signal\"\n\ntype conditionAndAction struct {\n\t// condition is a function pointer to a local activity\n\tcondition interface{}\n\t// action is a function pointer to a regular activity\n\taction interface{}\n}\n\nvar checks = []conditionAndAction{\n\t{checkCondition0, activityForCondition0},\n\t{checkCondition1, activityForCondition1},\n\t{checkCondition2, activityForCondition2},\n\t{checkCondition3, activityForCondition3},\n\t{checkCondition4, activityForCondition4},\n}\n\n// processingWorkflow is a workflow that process a given signal data. It evaluates if any conditions are meet for\n// the given signal data by using LocalActivity which runs as local function and then schedule activities to handle\n// it if the condition is meet. The idea is that you could have many conditions (for example 100 conditions) that needs\n// to be evaluated, and only a couple of them will meet the condition and needs to be processed by an activity. Using\n// local activity is very efficient in this case because local activity is execute as local function directly by decider\n// worker.\nfunc processingWorkflow(ctx workflow.Context, data string) (string, error) {\n\tlogger := workflow.GetLogger(ctx)\n\n\tlao := workflow.LocalActivityOptions{\n\t\t// use short timeout as local activity is execute as function locally.\n\t\tScheduleToCloseTimeout: time.Second,\n\t}\n\tctx = workflow.WithLocalActivityOptions(ctx, lao)\n\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tvar actionFutures []workflow.Future\n\n\tfor i, check := range checks {\n\t\tvar conditionMeet bool\n\t\terr := workflow.ExecuteLocalActivity(ctx, check.condition, data).Get(ctx, &conditionMeet)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tlogger.Sugar().Infof(\"condition meet for %v: %v\", i, conditionMeet)\n\t\tif conditionMeet {\n\t\t\tf := workflow.ExecuteActivity(ctx, check.action, data)\n\t\t\tactionFutures = append(actionFutures, f)\n\t\t}\n\t}\n\n\tvar processResult string\n\tfor _, f := range actionFutures {\n\t\tvar actionResult string\n\t\tif err := f.Get(ctx, &actionResult); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tprocessResult += actionResult\n\t}\n\n\treturn processResult, nil\n}\n\n// signalHandlingWorkflow is a workflow that waits on signal and then sends that signal to be processed by a child workflow.\nfunc signalHandlingWorkflow(ctx workflow.Context) error {\n\tlogger := workflow.GetLogger(ctx)\n\tch := workflow.GetSignalChannel(ctx, SignalName)\n\tfor {\n\t\tvar signal string\n\t\tif more := ch.Receive(ctx, &signal); !more {\n\t\t\tlogger.Info(\"Signal channel closed\")\n\t\t\treturn cadence.NewCustomError(\"signal_channel_closed\")\n\t\t}\n\n\t\tlogger.Info(\"Signal received.\", zap.String(\"signal\", signal))\n\n\t\tif signal == \"exit\" {\n\t\t\tbreak\n\t\t}\n\n\t\tcwo := workflow.ChildWorkflowOptions{\n\t\t\tExecutionStartToCloseTimeout: time.Minute,\n\t\t\t// TaskStartToCloseTimeout must be larger than all local activity execution time, because DecisionTask won't\n\t\t\t// return until all local activities completed.\n\t\t\tTaskStartToCloseTimeout: time.Second * 30,\n\t\t}\n\t\tchildCtx := workflow.WithChildOptions(ctx, cwo)\n\n\t\tvar processResult string\n\t\terr := workflow.ExecuteChildWorkflow(childCtx, processingWorkflow, signal).Get(childCtx, &processResult)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tlogger.Sugar().Infof(\"Processed signal: %v, result: %v\", signal, processResult)\n\t}\n\n\treturn nil\n}\n\nfunc checkCondition0(ctx context.Context, signal string) (bool, error) {\n\t// some real logic happen here...\n\treturn strings.Contains(signal, \"_0_\"), nil\n}\n\nfunc checkCondition1(ctx context.Context, signal string) (bool, error) {\n\t// some real logic happen here...\n\treturn strings.Contains(signal, \"_1_\"), nil\n}\n\nfunc checkCondition2(ctx context.Context, signal string) (bool, error) {\n\t// some real logic happen here...\n\treturn strings.Contains(signal, \"_2_\"), nil\n}\n\nfunc checkCondition3(ctx context.Context, signal string) (bool, error) {\n\t// some real logic happen here...\n\treturn strings.Contains(signal, \"_3_\"), nil\n}\n\nfunc checkCondition4(ctx context.Context, signal string) (bool, error) {\n\t// some real logic happen here...\n\treturn strings.Contains(signal, \"_4_\"), nil\n}\n\nfunc activityForCondition0(ctx context.Context, signal string) (string, error) {\n\tactivity.GetLogger(ctx).Info(\"process for condition 0\")\n\t// some real processing logic goes here\n\ttime.Sleep(time.Second * 2)\n\treturn \"processed_0\", nil\n}\n\nfunc activityForCondition1(ctx context.Context, signal string) (string, error) {\n\tactivity.GetLogger(ctx).Info(\"process for condition 1\")\n\t// some real processing logic goes here\n\ttime.Sleep(time.Second * 2)\n\treturn \"processed_1\", nil\n}\n\nfunc activityForCondition2(ctx context.Context, signal string) (string, error) {\n\tactivity.GetLogger(ctx).Info(\"process for condition 2\")\n\t// some real processing logic goes here\n\ttime.Sleep(time.Second * 2)\n\treturn \"processed_2\", nil\n}\n\nfunc activityForCondition3(ctx context.Context, signal string) (string, error) {\n\tactivity.GetLogger(ctx).Info(\"process for condition 3\")\n\t// some real processing logic goes here\n\ttime.Sleep(time.Second * 2)\n\treturn \"processed_3\", nil\n}\n\nfunc activityForCondition4(ctx context.Context, signal string) (string, error) {\n\tactivity.GetLogger(ctx).Info(\"process for condition 4\")\n\t// some real processing logic goes here\n\ttime.Sleep(time.Second * 2)\n\treturn \"processed_4\", nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/localactivity/local_activity_workflow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/mock\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\ntype UnitTestSuite struct {\n\tsuite.Suite\n\ttestsuite.WorkflowTestSuite\n\n\tenv *testsuite.TestWorkflowEnvironment\n}\n\nfunc TestUnitTestSuite(t *testing.T) {\n\tsuite.Run(t, new(UnitTestSuite))\n}\n\nfunc (s *UnitTestSuite) SetupTest() {\n\ts.env = s.NewTestWorkflowEnvironment()\n\ts.env.RegisterWorkflow(processingWorkflow)\n\ts.env.RegisterWorkflow(signalHandlingWorkflow)\n\ts.env.RegisterActivity(activityForCondition0)\n\ts.env.RegisterActivity(activityForCondition1)\n\ts.env.RegisterActivity(activityForCondition2)\n\ts.env.RegisterActivity(activityForCondition3)\n\ts.env.RegisterActivity(activityForCondition4)\n}\n\nfunc (s *UnitTestSuite) TearDownTest() {\n\ts.env.AssertExpectations(s.T())\n}\n\nfunc (s *UnitTestSuite) Test_ProcessingWorkflow_SingleAction() {\n\tsignalData := \"_1_\"\n\n\t// mock activityForCondition1 so it won't wait on real clock\n\ts.env.OnActivity(activityForCondition1, mock.Anything, signalData).Return(\"processed_1\", nil)\n\n\ts.env.ExecuteWorkflow(processingWorkflow, signalData)\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n\n\tvar actualResult string\n\ts.NoError(s.env.GetWorkflowResult(&actualResult))\n\ts.Equal(\"processed_1\", actualResult)\n}\n\nfunc (s *UnitTestSuite) Test_ProcessingWorkflow_MultiAction() {\n\tsignalData := \"_1_, _3_\"\n\n\t// mock activityForCondition1 so it won't wait on real clock\n\ts.env.OnActivity(activityForCondition1, mock.Anything, signalData).Return(\"processed_1\", nil)\n\ts.env.OnActivity(activityForCondition3, mock.Anything, signalData).Return(\"processed_3\", nil)\n\n\ts.env.ExecuteWorkflow(processingWorkflow, signalData)\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n\n\tvar actualResult string\n\ts.NoError(s.env.GetWorkflowResult(&actualResult))\n\ts.Equal(\"processed_1processed_3\", actualResult)\n}\n\nfunc (s *UnitTestSuite) Test_SignalHandlingWorkflow() {\n\ts.env.OnActivity(activityForCondition1, mock.Anything, \"_1_\").Return(\"processed_1\", nil)\n\n\ts.env.RegisterDelayedCallback(func() {\n\t\ts.env.SignalWorkflow(\"trigger-signal\", \"_1_\")\n\t}, time.Minute)\n\ts.env.RegisterDelayedCallback(func() {\n\t\ts.env.SignalWorkflow(\"trigger-signal\", \"exit\")\n\t}, time.Minute*2)\n\n\ts.env.ExecuteWorkflow(signalHandlingWorkflow)\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n}\n"
  },
  {
    "path": "cmd/samples/recipes/localactivity/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"localactivity_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute * 3,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t\tWorkflowIDReusePolicy:           client.WorkflowIDReusePolicyAllowDuplicate,\n\t}\n\th.StartWorkflow(workflowOptions, signalHandlingWorkflow)\n}\n\nfunc main() {\n\tvar mode, workflowID, signal string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker, trigger or query.\")\n\tflag.StringVar(&workflowID, \"w\", \"\", \"WorkflowID\")\n\tflag.StringVar(&signal, \"s\", \"signal_data\", \"SignalData\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(processingWorkflow)\n\t\th.RegisterWorkflow(signalHandlingWorkflow)\n\t\th.RegisterActivity(activityForCondition0)\n\t\th.RegisterActivity(activityForCondition1)\n\t\th.RegisterActivity(activityForCondition2)\n\t\th.RegisterActivity(activityForCondition3)\n\t\th.RegisterActivity(activityForCondition4)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\tcase \"signal\":\n\t\th.SignalWorkflow(workflowID, SignalName, signal)\n\t}\n}"
  },
  {
    "path": "cmd/samples/recipes/mutex/README.md",
    "content": "This mutex workflow demos an ability to lock/unlock a particular resource within a particular cadence domain\nso that other workflows within the same domain would wait until a resource lock is released. This is useful \nwhen we want to avoid race conditions or parallel mutually exclusive operations on the same resource.\n\nOne way of coordinating parallel processing is to use cadence signals with SignalWithStartWorkflow and\nmake sure signals are getting processed sequentially, however the logic might become too complex if we\nneed to lock two or more resources at the same time. Mutex workflow pattern can simplify that.\n\nThis example enqueues two long running SampleWorkflowWithMutex workflows in parallel. And each of the workflows has a mutex section. \nWhen SampleWorkflowWithMutex reaches Mutex section, it starts a mutex workflow via local activity, and blocks until\n\"acquire-lock-event\" is received. Once \"acquire-lock-event\" is received, it enters critical section,\nand finally releases the lock once processing is over by sending \"releaseLock\" a signal to the MutexWorkflow.\n\n\n### Steps to run this sample:\n1) You need a cadence service running. See details in cmd/samples/README.md\n2) Run the following command to start the worker\n\n    ```\n    ./bin/mutex -m worker\n    ```\n\n3) Run the following command to start the example\n\n    ```\n    ./bin/mutex -m trigger\n    ```\n  \nYou should see that second workflow critical section is executed when first workflow\ncritical operation is finished.\n"
  },
  {
    "path": "cmd/samples/recipes/mutex/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\nconst (\n\t// ApplicationName is the task list for this sample\n\tApplicationName = \"mutexExample\"\n\n\t_sampleHelperContextKey = \"sampleHelper\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope:              h.WorkerMetricScope,\n\t\tLogger:                    h.Logger,\n\t\tBackgroundActivityContext: context.WithValue(context.Background(), _sampleHelperContextKey, h),\n\t}\n\n\t// Start Worker.\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\n// startTwoWorkflows starts two workflows that operate on the same recourceID\nfunc startTwoWorkflows(h *common.SampleHelper) {\n\tresourceID := uuid.New()\n\th.StartWorkflow(client.StartWorkflowOptions{\n\t\tID:                              \"SampleWorkflowWithMutex_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    10 * time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t},\n\t\tsampleWorkflowWithMutex,\n\t\tresourceID)\n\th.StartWorkflow(client.StartWorkflowOptions{\n\t\tID:                              \"SampleWorkflowWithMutex_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    10 * time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t},\n\t\tsampleWorkflowWithMutex,\n\t\tresourceID)\n}\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(mutexWorkflow)\n\t\th.RegisterWorkflow(sampleWorkflowWithMutex)\n\t\th.RegisterActivity(signalWithStartMutexWorkflowActivity)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartTwoWorkflows(&h)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/mutex/mutex_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/mock\"\n\t\"go.uber.org/cadence\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/testsuite\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\nconst (\n\t// AcquireLockSignalName signal channel name for lock acquisition\n\tAcquireLockSignalName = \"acquire-lock-event\"\n\t// RequestLockSignalName channel name for request lock\n\tRequestLockSignalName = \"request-lock-event\"\n)\n\n// UnlockFunc ...\ntype UnlockFunc func() error\n\n// Mutex - cadence mutex\ntype Mutex struct {\n\tcurrentWorkflowID string\n\tlockNamespace     string\n}\n\n// NewMutex initializes cadence mutex\nfunc NewMutex(currentWorkflowID string, lockNamespace string) *Mutex {\n\treturn &Mutex{\n\t\tcurrentWorkflowID: currentWorkflowID,\n\t\tlockNamespace:     lockNamespace,\n\t}\n}\n\n// Lock - locks mutex\nfunc (s *Mutex) Lock(ctx workflow.Context,\n\tresourceID string, unlockTimeout time.Duration) (UnlockFunc, error) {\n\n\tactivityCtx := workflow.WithLocalActivityOptions(ctx, workflow.LocalActivityOptions{\n\t\tScheduleToCloseTimeout: time.Minute * 1,\n\t\tRetryPolicy: &cadence.RetryPolicy{\n\t\t\tInitialInterval:    time.Second,\n\t\t\tBackoffCoefficient: 2.0,\n\t\t\tMaximumInterval:    time.Minute,\n\t\t\tExpirationInterval: time.Minute * 10,\n\t\t\tMaximumAttempts:    5,\n\t\t},\n\t})\n\n\tvar releaseLockChannelName string\n\tvar execution workflow.Execution\n\terr := workflow.ExecuteLocalActivity(activityCtx,\n\t\tsignalWithStartMutexWorkflowActivity, s.lockNamespace,\n\t\tresourceID, s.currentWorkflowID, unlockTimeout).Get(ctx, &execution)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tworkflow.GetSignalChannel(ctx, AcquireLockSignalName).\n\t\tReceive(ctx, &releaseLockChannelName)\n\n\tunlockFunc := func() error {\n\t\treturn workflow.SignalExternalWorkflow(ctx, execution.ID, execution.RunID,\n\t\t\treleaseLockChannelName, \"releaseLock\").Get(ctx, nil)\n\t}\n\treturn unlockFunc, nil\n}\n\n// mutexWorkflow used for locking a resource\nfunc mutexWorkflow(\n\tctx workflow.Context,\n\tnamespace string,\n\tresourceID string,\n\tunlockTimeout time.Duration,\n) error {\n\tcurrentWorkflowID := workflow.GetInfo(ctx).WorkflowExecution.ID\n\tif currentWorkflowID == \"default-test-workflow-id\" {\n\t\t// unit testing hack, see https://github.com/uber-go/cadence-client/issues/663\n\t\tworkflow.Sleep(ctx, 10*time.Millisecond)\n\t}\n\tlogger := workflow.GetLogger(ctx).With(zap.String(\"currentWorkflowID\", currentWorkflowID))\n\tlogger.Info(\"started\")\n\tvar ack string\n\trequestLockCh := workflow.GetSignalChannel(ctx, RequestLockSignalName)\n\tfor {\n\t\tvar senderWorkflowID string\n\t\tif !requestLockCh.ReceiveAsync(&senderWorkflowID) {\n\t\t\tlogger.Info(\"no more signals\")\n\t\t\tbreak\n\t\t}\n\t\tvar releaseLockChannelName string\n\t\t_ = workflow.SideEffect(ctx, func(ctx workflow.Context) interface{} {\n\t\t\treturn _generateUnlockChannelName(senderWorkflowID)\n\t\t}).Get(&releaseLockChannelName)\n\t\tlogger := logger.With(zap.String(\"releaseLockChannelName\", releaseLockChannelName))\n\t\tlogger.Info(\"generated release lock channel name\")\n\t\t// Send release lock channel name back to a senderWorkflowID, so that it can\n\t\t// release the lock using release lock channel name\n\t\terr := workflow.SignalExternalWorkflow(ctx, senderWorkflowID, \"\",\n\t\t\tAcquireLockSignalName, releaseLockChannelName).Get(ctx, nil)\n\t\tif err != nil {\n\t\t\t// .Get(ctx, nil) blocks until the signal is sent.\n\t\t\t// If the senderWorkflowID is closed (terminated/canceled/timeouted/completed/etc), this would return error.\n\t\t\t// In this case we release the lock immediately instead of failing the mutex workflow.\n\t\t\t// Mutex workflow failing would lead to all workflows that have sent requestLock will be waiting.\n\t\t\tlogger.With(zap.Error(err)).Info(\"SignalExternalWorkflow error\")\n\t\t\tcontinue\n\t\t}\n\t\tlogger.With(zap.Error(err)).Info(\"signaled external workflow\")\n\t\tselector := workflow.NewSelector(ctx)\n\t\tselector.AddFuture(workflow.NewTimer(ctx, unlockTimeout), func(f workflow.Future) {\n\t\t\tlogger.Info(\"unlockTimeout exceeded\")\n\t\t})\n\t\tselector.AddReceive(workflow.GetSignalChannel(ctx, releaseLockChannelName), func(c workflow.Channel, more bool) {\n\t\t\tc.Receive(ctx, &ack)\n\t\t\tlogger.Info(\"release signal received\")\n\t\t})\n\t\tselector.Select(ctx)\n\t}\n\treturn nil\n}\n\n// signalWithStartMutexWorkflowActivity ...\nfunc signalWithStartMutexWorkflowActivity(\n\tctx context.Context,\n\tnamespace string,\n\tresourceID string,\n\tsenderWorkflowID string,\n\tunlockTimeout time.Duration,\n) (*workflow.Execution, error) {\n\n\th := ctx.Value(_sampleHelperContextKey).(*common.SampleHelper)\n\tworkflowID := fmt.Sprintf(\n\t\t\"%s:%s:%s\",\n\t\t\"mutex\",\n\t\tnamespace,\n\t\tresourceID,\n\t)\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              workflowID,\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Hour,\n\t\tDecisionTaskStartToCloseTimeout: time.Hour,\n\t\tRetryPolicy: &cadence.RetryPolicy{\n\t\t\tInitialInterval:    time.Second,\n\t\t\tBackoffCoefficient: 2.0,\n\t\t\tMaximumInterval:    time.Minute,\n\t\t\tExpirationInterval: time.Minute * 10,\n\t\t\tMaximumAttempts:    5,\n\t\t},\n\t\tWorkflowIDReusePolicy: client.WorkflowIDReusePolicyAllowDuplicate,\n\t}\n\twe := h.SignalWithStartWorkflowWithCtx(\n\t\tctx, workflowID, RequestLockSignalName, senderWorkflowID,\n\t\tworkflowOptions, mutexWorkflow, namespace, resourceID, unlockTimeout)\n\treturn we, nil\n}\n\n// _generateUnlockChannelName generates release lock channel name\nfunc _generateUnlockChannelName(senderWorkflowID string) string {\n\treturn fmt.Sprintf(\"unlock-event-%s\", senderWorkflowID)\n}\n\n// MockMutexLock stubs cadence mutex.Lock call\nfunc MockMutexLock(env *testsuite.TestWorkflowEnvironment, resourceID string, mockError error) {\n\tmockExecution := &workflow.Execution{ID: \"mockID\", RunID: \"mockRunID\"}\n\tenv.OnActivity(signalWithStartMutexWorkflowActivity,\n\t\tmock.Anything, mock.Anything, resourceID, mock.Anything, mock.Anything).\n\t\tReturn(mockExecution, mockError)\n\tenv.RegisterDelayedCallback(func() {\n\t\tenv.SignalWorkflow(AcquireLockSignalName, \"mockReleaseLockChannelName\")\n\t}, time.Millisecond*0)\n\tif mockError == nil {\n\t\tenv.OnSignalExternalWorkflow(mock.Anything, mock.Anything, mockExecution.RunID,\n\t\t\tmock.Anything, mock.Anything).Return(nil)\n\t}\n}\n\nfunc sampleWorkflowWithMutex(\n\tctx workflow.Context,\n\tresourceID string,\n) error {\n\tcurrentWorkflowID := workflow.GetInfo(ctx).WorkflowExecution.ID\n\tlogger := workflow.GetLogger(ctx).\n\t\tWith(zap.String(\"currentWorkflowID\", currentWorkflowID)).\n\t\tWith(zap.String(\"resourceID\", resourceID))\n\tlogger.Info(\"started\")\n\n\tmutex := NewMutex(currentWorkflowID, \"TestUseCase\")\n\tunlockFunc, err := mutex.Lock(ctx, resourceID, 10*time.Minute)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlogger.Info(\"resource locked\")\n\n\t// emulate long running process\n\tlogger.Info(\"critical operation started\")\n\tworkflow.Sleep(ctx, 10*time.Second)\n\tlogger.Info(\"critical operation finished\")\n\n\tunlockFunc()\n\n\tlogger.Info(\"finished\")\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/mutex/mutex_workflow_test.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/mock\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"go.uber.org/cadence/testsuite\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\ntype UnitTestSuite struct {\n\tsuite.Suite\n\ttestsuite.WorkflowTestSuite\n\n\tenv *testsuite.TestWorkflowEnvironment\n}\n\nfunc TestUnitTestSuite(t *testing.T) {\n\tsuite.Run(t, new(UnitTestSuite))\n}\n\nfunc (s *UnitTestSuite) SetupTest() {\n\ts.env = s.NewTestWorkflowEnvironment()\n\ts.env.RegisterWorkflow(mutexWorkflow)\n\ts.env.RegisterWorkflow(sampleWorkflowWithMutex)\n\ts.env.RegisterActivity(signalWithStartMutexWorkflowActivity)\n\n\tvar h common.SampleHelper\n\ts.env.SetWorkerOptions(worker.Options{\n\t\tBackgroundActivityContext: context.WithValue(context.Background(), _sampleHelperContextKey, h),\n\t})\n}\n\nfunc (s *UnitTestSuite) TearDownTest() {\n\ts.env.AssertExpectations(s.T())\n}\n\nfunc (s *UnitTestSuite) Test_Workflow_Success() {\n\tmockResourceID := \"mockResourceID\"\n\tMockMutexLock(s.env, mockResourceID, nil)\n\ts.env.ExecuteWorkflow(sampleWorkflowWithMutex, mockResourceID)\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n}\n\nfunc (s *UnitTestSuite) Test_Workflow_Error() {\n\tmockResourceID := \"mockResourceID\"\n\tMockMutexLock(s.env, mockResourceID, errors.New(\"bad-error\"))\n\ts.env.ExecuteWorkflow(sampleWorkflowWithMutex, mockResourceID)\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.EqualError(s.env.GetWorkflowError(), \"bad-error\")\n}\n\nfunc (s *UnitTestSuite) Test_MutexWorkflow_Success() {\n\tmockNamespace := \"mockNamespace\"\n\tmockResourceID := \"mockResourceID\"\n\tmockUnlockTimeout := 10 * time.Minute\n\tmockSenderWorkflowID := \"mockSenderWorkflowID\"\n\ts.env.RegisterDelayedCallback(func() {\n\t\ts.env.SignalWorkflow(RequestLockSignalName, mockSenderWorkflowID)\n\t}, time.Millisecond*0)\n\ts.env.RegisterDelayedCallback(func() {\n\t\ts.env.SignalWorkflow(\"unlock-event-mockSenderWorkflowID\", \"releaseLock\")\n\t}, time.Millisecond*0)\n\ts.env.OnSignalExternalWorkflow(mock.Anything, mockSenderWorkflowID, \"\",\n\t\tAcquireLockSignalName, mock.Anything).Return(nil)\n\n\ts.env.ExecuteWorkflow(\n\t\tmutexWorkflow,\n\t\tmockNamespace,\n\t\tmockResourceID,\n\t\tmockUnlockTimeout,\n\t)\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n}\n\nfunc (s *UnitTestSuite) Test_MutexWorkflow_TimeoutSuccess() {\n\tmockNamespace := \"mockNamespace\"\n\tmockResourceID := \"mockResourceID\"\n\tmockUnlockTimeout := 10 * time.Minute\n\tmockSenderWorkflowID := \"mockSenderWorkflowID\"\n\ts.env.RegisterDelayedCallback(func() {\n\t\ts.env.SignalWorkflow(RequestLockSignalName, mockSenderWorkflowID)\n\t}, time.Millisecond*0)\n\ts.env.OnSignalExternalWorkflow(mock.Anything, mockSenderWorkflowID, \"\",\n\t\tAcquireLockSignalName, mock.Anything).Return(nil)\n\n\ts.env.ExecuteWorkflow(\n\t\tmutexWorkflow,\n\t\tmockNamespace,\n\t\tmockResourceID,\n\t\tmockUnlockTimeout,\n\t)\n\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n}\n"
  },
  {
    "path": "cmd/samples/recipes/pickfirst/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\n\t// Start Worker.\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"pickfirst_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, samplePickFirstWorkflow)\n}\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(samplePickFirstWorkflow)\n\t\th.RegisterActivity(sampleActivity)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/pickfirst/pickfirst_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n)\n\n/**\n * This sample workflow execute activities in parallel branches, pick the result of the branch that completes first,\n * and then cancels other activities that are not finished yet.\n */\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"pickfirstGroup\"\n\n// samplePickFirstWorkflow workflow decider\nfunc samplePickFirstWorkflow(ctx workflow.Context) error {\n\tselector := workflow.NewSelector(ctx)\n\tvar firstResponse string\n\n\t// Use one cancel handler to cancel all of them. Cancelling on parent handler will close all the child ones\n\t// as well.\n\tchildCtx, cancelHandler := workflow.WithCancel(ctx)\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t\tWaitForCancellation:    true, // Wait for cancellation to complete.\n\t}\n\tchildCtx = workflow.WithActivityOptions(childCtx, ao)\n\n\t// Set WaitForCancellation to true to demonstrate the cancellation to the other activities. In real world case,\n\t// you might not care about them and could set WaitForCancellation to false (which is default value).\n\n\t// starts 2 activities in parallel\n\tf1 := workflow.ExecuteActivity(childCtx, sampleActivity, 0, time.Second*2)\n\tf2 := workflow.ExecuteActivity(childCtx, sampleActivity, 1, time.Second*10)\n\tpendingFutures := []workflow.Future{f1, f2}\n\tselector.AddFuture(f1, func(f workflow.Future) {\n\t\tf.Get(ctx, &firstResponse)\n\t}).AddFuture(f2, func(f workflow.Future) {\n\t\tf.Get(ctx, &firstResponse)\n\t})\n\n\t// wait for any of the future to complete\n\tselector.Select(ctx)\n\n\t// now at least one future is complete, so cancel all other pending futures.\n\tcancelHandler()\n\n\t// - If you want to wait for pending activities to finish after issuing cancellation\n\t// then wait for the future to complete.\n\t// - if you don't want to wait for completion of pending activities cancellation then you can choose to\n\t// set WaitForCancellation to false through WithWaitForCancellation(false)\n\tfor _, f := range pendingFutures {\n\t\tf.Get(ctx, nil)\n\t}\n\tworkflow.GetLogger(ctx).Info(\"Workflow completed.\")\n\treturn nil\n}\n\nfunc sampleActivity(ctx context.Context, currentBranchID int, totalDuration time.Duration) (string, error) {\n\n\tlogger := activity.GetLogger(ctx)\n\telapsedDuration := time.Nanosecond\n\tfor elapsedDuration < totalDuration {\n\t\ttime.Sleep(time.Second)\n\t\telapsedDuration += time.Second\n\n\t\t// record heartbeat every second to check if we are been cancelled\n\t\tactivity.RecordHeartbeat(ctx, \"status-report-to-workflow\")\n\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\t// We have been cancelled.\n\t\t\tmsg := fmt.Sprintf(\"Branch %d is cancelled.\", currentBranchID)\n\t\t\tlogger.Info(msg)\n\t\t\treturn msg, ctx.Err()\n\t\tdefault:\n\t\t\t// We are not cancelled yet.\n\t\t}\n\n\t\t// Do some custom work\n\t\t// ...\n\t}\n\n\tmsg := fmt.Sprintf(\"Branch %d done in %s.\", currentBranchID, totalDuration)\n\treturn msg, nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/pickfirst/pickfirst_workflow_test.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/mock\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\ntype UnitTestSuite struct {\n\tsuite.Suite\n\ttestsuite.WorkflowTestSuite\n\n\tenv *testsuite.TestWorkflowEnvironment\n}\n\nfunc TestUnitTestSuite(t *testing.T) {\n\tsuite.Run(t, new(UnitTestSuite))\n}\n\nfunc (s *UnitTestSuite) SetupTest() {\n\ts.env = s.NewTestWorkflowEnvironment()\n\ts.env.RegisterWorkflow(samplePickFirstWorkflow)\n\ts.env.RegisterActivity(sampleActivity)\n}\n\nfunc (s *UnitTestSuite) TearDownTest() {\n\ts.env.AssertExpectations(s.T())\n}\n\nfunc (s *UnitTestSuite) Test_Workflow() {\n\ts.env.OnActivity(sampleActivity, mock.Anything, mock.Anything, mock.Anything).\n\t\tReturn(func(ctx context.Context, currentBranchID int, totalDuration time.Duration) (string, error) {\n\t\t\t// make branch 0 super fast so we don't have to wait sleep time in unit test\n\t\t\tif currentBranchID == 0 {\n\t\t\t\ttotalDuration = time.Nanosecond\n\t\t\t}\n\t\t\treturn sampleActivity(ctx, currentBranchID, totalDuration)\n\t\t}).Once()\n\n\ts.env.ExecuteWorkflow(samplePickFirstWorkflow)\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n}\n"
  },
  {
    "path": "cmd/samples/recipes/query/README.md",
    "content": "This sample workflow demos how to use query API to get the current state of running workflow.\n\nquery_workflow.go shows how to setup a custom workflow query handler\nquery_workflow_test.go shows how to unit-test query functionality\n\nSteps to run this sample:\n1) You need a cadence service running. See details in cmd/samples/README.md\n2) Run the following command to start worker\n```\n./bin/query -m worker\n```\n3) Run the following command to trigger a workflow execution. You should see workflowID and runID print out on screen.\n```\n./bin/query -m trigger\n```\n4) Run \"./bin/query -m query -w my_workflow_id -r my_run_id -t state\" replace my_workflow_id and my_run_id with the workflowID and runID that you see in step 3. You should see current workflow state print on screen.\n```\n./bin/query -m query -w <workflow_id from step 3> -r <run_id from step 3> -t state\n```\n5) You could also replace the query type \"state\" to \"__stack_trace\" (replace -t state to -t __stack_trace) to dump the call stack for the workflow.\n```\n./bin/query -m query -w <workflow_id from step 3> -r <run_id from step 3> -t __stack_trace\n```\n"
  },
  {
    "path": "cmd/samples/recipes/query/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"query_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Hour * 10,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, queryWorkflow)\n}\n\nfunc main() {\n\tvar mode, workflowID, runID, queryType string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker, trigger or query.\")\n\tflag.StringVar(&workflowID, \"w\", \"\", \"WorkflowID\")\n\tflag.StringVar(&runID, \"r\", \"\", \"RunID\")\n\tflag.StringVar(&queryType, \"t\", \"__stack_trace\", \"QueryType\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(queryWorkflow)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\tcase \"query\":\n\t\th.QueryWorkflow(workflowID, runID, queryType)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/query/query_workflow.go",
    "content": "package main\n\nimport (\n\t\"time\"\n\n\t\"go.uber.org/cadence/workflow\"\n)\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"queryGroup\"\n\n// queryWorkflow is an implementation of cadence workflow to demo how to setup query handler\nfunc queryWorkflow(ctx workflow.Context) error {\n\tqueryResult := \"started\"\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"QueryWorkflow started\")\n\t// setup query handler for query type \"state\"\n\terr := workflow.SetQueryHandler(ctx, \"state\", func(input []byte) (string, error) {\n\t\treturn queryResult, nil\n\t})\n\tif err != nil {\n\t\tlogger.Info(\"SetQueryHandler failed: \" + err.Error())\n\t\treturn err\n\t}\n\n\tqueryResult = \"waiting on timer\"\n\t// to simulate workflow been blocked on something, in reality, workflow could wait on anything like activity, signal or timer\n\tworkflow.NewTimer(ctx, time.Minute*2).Get(ctx, nil)\n\tlogger.Info(\"Timer fired\")\n\n\tqueryResult = \"done\"\n\tlogger.Info(\"QueryWorkflow completed\")\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/query/query_workflow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\nfunc Test_QueryWorkflow(t *testing.T) {\n\tts := &testsuite.WorkflowTestSuite{}\n\tenv := ts.NewTestWorkflowEnvironment()\n\tenv.RegisterWorkflow(queryWorkflow)\n\n\tw := false\n\tenv.RegisterDelayedCallback(func() {\n\t\tqueryAndVerify(t, env, \"waiting on timer\")\n\t\tw = true\n\t}, time.Minute*1)\n\n\tenv.ExecuteWorkflow(queryWorkflow)\n\trequire.True(t, env.IsWorkflowCompleted())\n\trequire.NoError(t, env.GetWorkflowError())\n\trequire.True(t, w, \"state at timer not verified\")\n\tqueryAndVerify(t, env, \"done\")\n}\n\nfunc queryAndVerify(t *testing.T, env *testsuite.TestWorkflowEnvironment, expectedState string) {\n\tresult, err := env.QueryWorkflow(\"state\")\n\trequire.NoError(t, err)\n\tvar state string\n\terr = result.Get(&state)\n\trequire.NoError(t, err)\n\trequire.Equal(t, expectedState, state)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/retryactivity/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"retry_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, retryWorkflow)\n}\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(retryWorkflow)\n\t\th.RegisterActivity(batchProcessingActivity)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/retryactivity/retry_activity_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"go.uber.org/cadence\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * This sample workflow executes unreliable activity with retry policy. If activity execution failed, server will\n * schedule retry based on retry policy configuration. The activity also heartbeat progress so it could resume from\n * reported progress in retry attempt.\n */\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"retryactivityGroup\"\n\n// retryWorkflow workflow decider\nfunc retryWorkflow(ctx workflow.Context) error {\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute * 10,\n\t\tHeartbeatTimeout:       time.Second * 10,\n\t\tRetryPolicy: &cadence.RetryPolicy{\n\t\t\tInitialInterval:          time.Second,\n\t\t\tBackoffCoefficient:       2.0,\n\t\t\tMaximumInterval:          time.Minute,\n\t\t\tExpirationInterval:       time.Minute * 5,\n\t\t\tMaximumAttempts:          5,\n\t\t\tNonRetriableErrorReasons: []string{\"bad-error\"},\n\t\t},\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\terr := workflow.ExecuteActivity(ctx, batchProcessingActivity, 0, 20, time.Second).Get(ctx, nil)\n\tif err != nil {\n\t\tworkflow.GetLogger(ctx).Info(\"Workflow completed with error.\", zap.Error(err))\n\t\treturn err\n\t}\n\tworkflow.GetLogger(ctx).Info(\"Workflow completed.\")\n\treturn nil\n}\n\n// batchProcessingActivity process batchSize of jobs starting from firstTaskID. This activity will heartbeat to report\n// progress, and it could fail sometimes. Use retry policy to retry when it failed, and resume from reported progress.\nfunc batchProcessingActivity(ctx context.Context, firstTaskID, batchSize int, processDelay time.Duration) error {\n\tlogger := activity.GetLogger(ctx)\n\n\ti := firstTaskID\n\tif activity.HasHeartbeatDetails(ctx) {\n\t\t// we are retry from a failed attempt, and there is reported progress that we should resume from.\n\t\tvar completedIdx int\n\t\tif err := activity.GetHeartbeatDetails(ctx, &completedIdx); err == nil {\n\t\t\ti = completedIdx + 1\n\t\t\tlogger.Info(\"Resuming from failed attempt\", zap.Int(\"ReportedProgress\", completedIdx))\n\t\t}\n\t}\n\n\ttaskProcessedInThisAttempt := 0 // used to determine when to fail (simulate failure)\n\tfor ; i < firstTaskID+batchSize; i++ {\n\t\t// process task i\n\t\tlogger.Info(\"processing task\", zap.Int(\"TaskID\", i))\n\t\ttime.Sleep(processDelay) // simulate time spend on processing each task\n\t\tactivity.RecordHeartbeat(ctx, i)\n\t\ttaskProcessedInThisAttempt++\n\n\t\t// simulate failure after process 1/3 of the tasks\n\t\tif taskProcessedInThisAttempt >= batchSize/3 && i < firstTaskID+batchSize-1 {\n\t\t\tlogger.Info(\"Activity failed, will retry...\")\n\t\t\t// Activity could return different error types for different failures so workflow could handle them differently.\n\t\t\t// For example, decide to retry or not based on error reasons.\n\t\t\treturn cadence.NewCustomError(\"some-retryable-error\")\n\t\t}\n\t}\n\n\tlogger.Info(\"Activity succeed.\")\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/retryactivity/retry_activity_workflow_test.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/mock\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\ntype UnitTestSuite struct {\n\tsuite.Suite\n\ttestsuite.WorkflowTestSuite\n\n\tenv *testsuite.TestWorkflowEnvironment\n}\n\nfunc TestUnitTestSuite(t *testing.T) {\n\tsuite.Run(t, new(UnitTestSuite))\n}\n\nfunc (s *UnitTestSuite) SetupTest() {\n\ts.env = s.NewTestWorkflowEnvironment()\n\ts.env.RegisterWorkflow(retryWorkflow)\n\ts.env.RegisterActivity(batchProcessingActivity)\n}\n\nfunc (s *UnitTestSuite) TearDownTest() {\n\ts.env.AssertExpectations(s.T())\n}\n\nfunc (s *UnitTestSuite) Test_Workflow() {\n\tvar startedIDs []int\n\ts.env.OnActivity(batchProcessingActivity, mock.Anything, mock.Anything, mock.Anything, mock.Anything).\n\t\tReturn(func(ctx context.Context, firstTaskID, batchSize int, processDelay time.Duration) error {\n\t\t\ti := firstTaskID\n\t\t\tif activity.HasHeartbeatDetails(ctx) {\n\t\t\t\tvar completedIdx int\n\t\t\t\tif err := activity.GetHeartbeatDetails(ctx, &completedIdx); err == nil {\n\t\t\t\t\ti = completedIdx + 1\n\t\t\t\t}\n\t\t\t}\n\t\t\tstartedIDs = append(startedIDs, i)\n\n\t\t\treturn batchProcessingActivity(ctx, firstTaskID, batchSize, time.Nanosecond /* override for test */)\n\t\t})\n\n\ts.env.ExecuteWorkflow(retryWorkflow)\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n\ts.Equal([]int{0, 6, 12, 18}, startedIDs)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/searchattributes/README.md",
    "content": "This sample shows how to use search attributes. (Note this feature only works when running Cadence with ElasticSearch)\n\nSteps to run this sample:\n1) You need a cadence service with Elasticsearch running. The easiest way is by running:\n```\ndocker-compose -f docker-compose-es.yml up\n```\n\nFor details, see https://github.com/uber/cadence/blob/master/docker/README.md\n\n2) Run the following command to start worker\n```\n./bin/searchattributes -m worker\n```\n3) Run the following command to trigger a workflow execution. You should see workflowID and runID print out on screen.\n```\n./bin/searchattributes -m trigger\n```\n"
  },
  {
    "path": "cmd/samples/recipes/searchattributes/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/zap\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\tworkflowClient, err := h.Builder.BuildCadenceClient()\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to build cadence client.\", zap.Error(err))\n\t\tpanic(err)\n\t}\n\tctx := context.WithValue(context.Background(), CadenceClientKey, workflowClient)\n\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope:              h.WorkerMetricScope,\n\t\tLogger:                    h.Logger,\n\t\tBackgroundActivityContext: ctx,\n\t}\n\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"searchAttributes_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t\tSearchAttributes:                getSearchAttributesForStart(), // optional search attributes when start workflow\n\t}\n\th.StartWorkflow(workflowOptions, searchAttributesWorkflow)\n}\n\nfunc getSearchAttributesForStart() map[string]interface{} {\n\treturn map[string]interface{}{\n\t\t\"CustomIntField\": 1,\n\t}\n}\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(searchAttributesWorkflow)\n\t\th.RegisterActivity(listExecutions)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/searchattributes/searchattributes_workflow.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/.gen/go/shared\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n/**\n * This sample shows how to use search attributes. (Note this feature only work with ElasticSearch)\n */\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"searchAttributesGroup\"\n\n// ClientKey is the key for lookup\ntype ClientKey int\n\nconst (\n\t// DomainName used for this sample\n\tDomainName = \"default\"\n\t// CadenceClientKey for retrieving cadence client from context\n\tCadenceClientKey ClientKey = iota\n)\n\nvar (\n\t// ErrCadenceClientNotFound when cadence client is not found on context\n\tErrCadenceClientNotFound = errors.New(\"failed to retrieve cadence client from context\")\n)\n\n// searchAttributesWorkflow workflow decider\nfunc searchAttributesWorkflow(ctx workflow.Context) error {\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"SearchAttributes workflow started\")\n\n\t// get search attributes that provided when start workflow\n\tinfo := workflow.GetInfo(ctx)\n\tval := info.SearchAttributes.IndexedFields[\"CustomIntField\"]\n\tvar currentIntValue int\n\terr := client.NewValue(val).Get(&currentIntValue)\n\tif err != nil {\n\t\tlogger.Error(\"Get search attribute failed\", zap.Error(err))\n\t\treturn err\n\t}\n\tlogger.Info(\"Current Search Attributes: \", zap.String(\"CustomIntField\", strconv.Itoa(currentIntValue)))\n\n\t// upsert search attributes\n\tattributes := map[string]interface{}{\n\t\t\"CustomIntField\":      2, // update CustomIntField from 1 to 2, then insert other fields\n\t\t\"CustomKeywordField\":  \"Update1\",\n\t\t\"CustomBoolField\":     true,\n\t\t\"CustomDoubleField\":   3.14,\n\t\t\"CustomDatetimeField\": time.Date(2019, 1, 1, 0, 0, 0, 0, time.Local),\n\t\t\"CustomStringField\":   \"String field is for text. When query, it will be tokenized for partial match. StringTypeField cannot be used in Order By\",\n\t}\n\tworkflow.UpsertSearchAttributes(ctx, attributes)\n\n\t// print current search attributes\n\tinfo = workflow.GetInfo(ctx)\n\terr = printSearchAttributes(info.SearchAttributes, logger)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// update search attributes again\n\tattributes = map[string]interface{}{\n\t\t\"CustomKeywordField\": \"Update2\",\n\t}\n\tworkflow.UpsertSearchAttributes(ctx, attributes)\n\n\t// print current search attributes\n\tinfo = workflow.GetInfo(ctx)\n\terr = printSearchAttributes(info.SearchAttributes, logger)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tworkflow.Sleep(ctx, 2*time.Second) // wait update reflected on ElasticSearch\n\n\t// list workflow\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: 2 * time.Minute,\n\t\tStartToCloseTimeout:    2 * time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\tquery := \"CustomIntField=2 and CustomKeywordField='Update2' order by CustomDatetimeField DESC\"\n\tvar listResults []*shared.WorkflowExecutionInfo\n\terr = workflow.ExecuteActivity(ctx, listExecutions, query).Get(ctx, &listResults)\n\tif err != nil {\n\t\tlogger.Error(\"Failed to list workflow executions.\", zap.Error(err))\n\t\treturn err\n\t}\n\n\tlogger.Info(\"Workflow completed.\", zap.String(\"Execution\", listResults[0].String()))\n\n\treturn nil\n}\n\nfunc printSearchAttributes(searchAttributes *shared.SearchAttributes, logger *zap.Logger) error {\n\tbuf := new(bytes.Buffer)\n\tfor k, v := range searchAttributes.IndexedFields {\n\t\tvar currentVal interface{}\n\t\terr := client.NewValue(v).Get(&currentVal)\n\t\tif err != nil {\n\t\t\tlogger.Error(fmt.Sprintf(\"Get search attribute for key %s failed\", k), zap.Error(err))\n\t\t\treturn err\n\t\t}\n\t\tfmt.Fprintf(buf, \"%s=%v\\n\", k, currentVal)\n\t}\n\tlogger.Info(fmt.Sprintf(\"Current Search Attributes: \\n%s\", buf.String()))\n\treturn nil\n}\n\nfunc listExecutions(ctx context.Context, query string) ([]*shared.WorkflowExecutionInfo, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"List executions.\", zap.String(\"Query\", query))\n\n\tcadenceClient, err := getCadenceClientFromContext(ctx)\n\tif err != nil {\n\t\tlogger.Error(\"Error when get cadence client\")\n\t\treturn nil, err\n\t}\n\n\tvar executions []*shared.WorkflowExecutionInfo\n\tvar nextPageToken []byte\n\tfor hasMore := true; hasMore; hasMore = len(nextPageToken) > 0 {\n\t\tresp, err := cadenceClient.ListWorkflow(ctx, &shared.ListWorkflowExecutionsRequest{\n\t\t\tDomain:        common.StringPtr(DomainName),\n\t\t\tPageSize:      common.Int32Ptr(10),\n\t\t\tNextPageToken: nextPageToken,\n\t\t\tQuery:         common.StringPtr(query),\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfor _, r := range resp.Executions {\n\t\t\texecutions = append(executions, r)\n\t\t}\n\n\t\tnextPageToken = resp.NextPageToken\n\t\tactivity.RecordHeartbeat(ctx, nextPageToken)\n\t}\n\n\treturn executions, nil\n}\n\nfunc getCadenceClientFromContext(ctx context.Context) (client.Client, error) {\n\tlogger := activity.GetLogger(ctx)\n\tcadenceClient := ctx.Value(CadenceClientKey).(client.Client)\n\tif cadenceClient == nil {\n\t\tlogger.Error(\"Could not retrieve cadence client from context.\")\n\t\treturn nil, ErrCadenceClientNotFound\n\t}\n\n\treturn cadenceClient, nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/searchattributes/searchattributes_workflow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/mock\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/cadence/.gen/go/shared\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\nfunc Test_Workflow(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\tenv := testSuite.NewTestWorkflowEnvironment()\n\tenv.RegisterWorkflow(searchAttributesWorkflow)\n\tenv.RegisterActivity(listExecutions)\n\n\t// mock search attributes on start\n\tenv.SetSearchAttributesOnStart(getSearchAttributesForStart())\n\n\t// mock upsert operations\n\tattributes := map[string]interface{}{\n\t\t\"CustomIntField\":      2, // update CustomIntField from 1 to 2, then insert other fields\n\t\t\"CustomKeywordField\":  \"Update1\",\n\t\t\"CustomBoolField\":     true,\n\t\t\"CustomDoubleField\":   3.14,\n\t\t\"CustomDatetimeField\": time.Date(2019, 1, 1, 0, 0, 0, 0, time.Local),\n\t\t\"CustomStringField\":   \"String field is for text. When query, it will be tokenized for partial match. StringTypeField cannot be used in Order By\",\n\t}\n\tenv.OnUpsertSearchAttributes(attributes).Return(nil).Once()\n\n\tattributes = map[string]interface{}{\n\t\t\"CustomKeywordField\": \"Update2\",\n\t}\n\tenv.OnUpsertSearchAttributes(attributes).Return(nil).Once()\n\n\t// mock activity\n\tenv.OnActivity(listExecutions, mock.Anything, mock.Anything).Return([]*shared.WorkflowExecutionInfo{{}}, nil).Once()\n\n\tenv.ExecuteWorkflow(searchAttributesWorkflow)\n\trequire.True(t, env.IsWorkflowCompleted())\n\trequire.NoError(t, env.GetWorkflowError())\n}\n"
  },
  {
    "path": "cmd/samples/recipes/sideeffect/sideeffect_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\nconst ApplicationName = \"HelloSideEffect\"\n\nfunc SideEffectWorkflow(ctx workflow.Context) error {\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"SideEffectWorkflow started\")\n\t// setup query handler for query type \"state\"\n\n\tvalue := \"\"\n\n\terr := workflow.SetQueryHandler(ctx, \"value\", func(input []byte) (string, error) {\n\t\treturn value, nil\n\t})\n\tif err != nil {\n\t\tlogger.Info(\"SetQueryHandler failed: \" + err.Error())\n\t\treturn err\n\t}\n\n\tworkflow.SideEffect(ctx, func(ctx workflow.Context) interface{} {\n\t\treturn uuid.New().String()\n\t}).Get(&value)\n\tlogger.Info(\"SideEffect value: \" + value)\n\n\tlogger.Info(\"SideEffectWorkflow completed\")\n\treturn nil\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"sideffectflow\",\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t\tWorkflowIDReusePolicy:           client.WorkflowIDReusePolicyAllowDuplicate,\n\t}\n\th.StartWorkflow(workflowOptions, SideEffectWorkflow)\n}\n\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc main() {\n\n\t//workflow.Register(SideEffectWorkflow)\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\th.RegisterWorkflow(SideEffectWorkflow)\n\n\tstartWorkers(&h)\n\n\t// The workers are supposed to be long running process that should not exit.\n\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\tstartWorkflow(&h)\n\n\tworkflowClient, err := h.Builder.BuildCadenceClient()\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to build cadence client.\", zap.Error(err))\n\t\tpanic(err)\n\t}\n\n\tresp, err := workflowClient.QueryWorkflow(context.Background(), \"sideffectflow\", \"\", \"value\")\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to query workflow\", zap.Error(err))\n\t\tpanic(\"Failed to query workflow.\")\n\t}\n\n\tvar result interface{}\n\tif err := resp.Get(&result); err != nil {\n\t\th.Logger.Error(\"Failed to decode query result\", zap.Error(err))\n\t}\n\th.Logger.Info(\"Received query result\", zap.Any(\"Result\", result))\n}\n"
  },
  {
    "path": "cmd/samples/recipes/signalcounter/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"signal_counter_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Hour,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, sampleSignalCounterWorkflow, 0)\n}\n\nfunc main() {\n\tvar mode string\n\tvar workflowID string\n\tvar signalValue int\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker, trigger(start a new workflow), or signal.\")\n\tflag.StringVar(&workflowID, \"w\", \"\", \"the workflowID to send signal\")\n\tflag.IntVar(&signalValue, \"sig\", 1, \"the value that is sent to the counter workflow\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(sampleSignalCounterWorkflow)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\tcase \"signal\":\n\t\th.SignalWorkflow(workflowID, \"channelA\", signalValue)\n\t\th.SignalWorkflow(workflowID, \"channelB\", signalValue)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/signalcounter/signal_counter_workflow.go",
    "content": "package main\n\nimport (\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * This sample workflow continuously counting signals and do continue as new\n */\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"signal_counter\"\n\n// A workflow execution cannot receive infinite number of signals due to history limit\n// By default 10000 is MaximumSignalsPerExecution which can be configured by DynamicConfig of Cadence cluster.\n// But it's recommended to do continueAsNew after receiving certain number of signals(in production, use a number <1000)\nconst maxSignalsPerExecution = 3\n\n// sampleSignalCounterWorkflow Workflow Decider.\nfunc sampleSignalCounterWorkflow(ctx workflow.Context, counter int) error {\n\n\tvar drainedAllSignals bool\n\tsignalsPerExecution := 0\n\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"Started SignalCounterWorkflow\")\n\n\tfor {\n\t\ts := workflow.NewSelector(ctx)\n\t\ts.AddReceive(workflow.GetSignalChannel(ctx, \"channelA\"), func(c workflow.Channel, ok bool) {\n\t\t\tif ok {\n\t\t\t\tvar i int\n\t\t\t\tc.Receive(ctx, &i)\n\t\t\t\tcounter += i\n\t\t\t\tsignalsPerExecution += 1\n\t\t\t\tlogger.Info(\"Received signal on channelA.\", zap.Int(\"Counter\", i))\n\t\t\t}\n\t\t})\n\t\ts.AddReceive(workflow.GetSignalChannel(ctx, \"channelB\"), func(c workflow.Channel, ok bool) {\n\t\t\tif ok {\n\t\t\t\tvar i int\n\t\t\t\tc.Receive(ctx, &i)\n\t\t\t\tcounter += i\n\t\t\t\tsignalsPerExecution += 1\n\t\t\t\tlogger.Info(\"Received signal on channelB.\", zap.Int(\"Counter\", i))\n\t\t\t}\n\t\t})\n\n\t\tif signalsPerExecution >= maxSignalsPerExecution {\n\t\t\ts.AddDefault(func() {\n\t\t\t\t// this indicate that we have drained all signals within the decision task, and it's safe to do a continueAsNew\n\t\t\t\tdrainedAllSignals = true\n\t\t\t\tlogger.Info(\"Reached maxSignalsPerExecution limit\")\n\t\t\t})\n\t\t}\n\n\t\ts.Select(ctx)\n\n\t\tif drainedAllSignals {\n\t\t\tlogger.Info(\"Returning ContinueAsNewError\")\n\t\t\treturn workflow.NewContinueAsNewError(ctx, sampleSignalCounterWorkflow, counter)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/signalcounter/workflow_test.go",
    "content": "package main\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/suite\"\n\t\"go.uber.org/cadence/testsuite\"\n\t\"go.uber.org/cadence/workflow\"\n)\n\ntype UnitTestSuite struct {\n\tsuite.Suite\n\ttestsuite.WorkflowTestSuite\n\n\tenv *testsuite.TestWorkflowEnvironment\n}\n\nfunc TestUnitTestSuite(t *testing.T) {\n\tsuite.Run(t, new(UnitTestSuite))\n}\n\nfunc (s *UnitTestSuite) SetupTest() {\n\ts.env = s.NewTestWorkflowEnvironment()\n\ts.env.RegisterWorkflow(sampleSignalCounterWorkflow)\n}\n\nfunc (s *UnitTestSuite) TearDownTest() {\n\ts.env.AssertExpectations(s.T())\n}\n\nfunc (s *UnitTestSuite) Test_SampleSignalCounterWorkflow() {\n\n\tfor i:=0; i< 11; i++{\n\t\ts.env.RegisterDelayedCallback(func() {\n\t\t\ts.env.SignalWorkflow(\"channelA\", 10)\n\t\t}, 0)\n\t}\n\n\ts.env.ExecuteWorkflow(sampleSignalCounterWorkflow, 0)\n\n\ts.True(s.env.IsWorkflowCompleted())\n\terr, ok := s.env.GetWorkflowError().(*workflow.ContinueAsNewError)\n\ts.True(ok)\n\ts.True(strings.Contains(err.WorkflowType().Name, \"sampleSignalCounterWorkflow\"))\n\t// It should receive and process all 11 signals, even though maxSignalsPerExecution is 10\n\ts.Equal(110, err.Args()[0])\n}\n"
  },
  {
    "path": "cmd/samples/recipes/sleep/README.md",
    "content": "# Sleep Workflow Sample\n\nThis sample workflow demonstrates how to use the `workflow.Sleep` function in Cadence workflows. The sleep functionality allows workflows to pause execution for a specified duration before continuing with subsequent activities.\n\n## Sample Description\n\nThe sample workflow:\n- Takes a sleep duration as input parameter\n- Uses `workflow.Sleep` to pause workflow execution for the specified duration\n- Executes a main activity after the sleep period completes\n- Demonstrates proper error handling for sleep operations\n- Shows how to configure activity options for post-sleep activities\n\nThe workflow is useful for scenarios where you need to:\n- Implement delays or timeouts in workflow logic\n- Wait for external events or conditions\n- Implement retry mechanisms with exponential backoff\n- Create scheduled or periodic workflows\n\n## Key Components\n\n- **Workflow**: `sleepWorkflow` demonstrates the sleep functionality with activity execution\n- **Activity**: `mainSleepActivity` is executed after the sleep period\n- **Sleep Duration**: Configurable duration (default: 30 seconds) passed as workflow input\n- **Test**: Includes unit tests to verify sleep and activity execution\n\n## Steps to Run Sample\n\n1. You need a cadence service running. See details in cmd/samples/README.md\n\n2. Run the following command to start the worker:\n   ```\n   ./bin/sleep -m worker\n   ```\n\n3. Run the following command to execute the workflow:\n   ```\n   ./bin/sleep -m trigger\n   ```\n\nYou should see logs showing:\n- Workflow start with sleep duration\n- Sleep completion message\n- Main activity execution\n- Workflow completion\n\n## Customization\n\nTo modify the sleep behavior:\n- Change the `sleepDuration` in `main.go` to adjust the default sleep time\n- Modify the activity options to configure timeouts for post-sleep activities\n- Add additional activities or logic after the sleep period\n- Implement conditional sleep based on workflow state\n\n## Use Cases\n\nThis pattern is useful for:\n- **Scheduled Tasks**: Implement workflows that need to wait before processing\n- **Rate Limiting**: Add delays between API calls or external service interactions\n- **Retry Logic**: Implement exponential backoff for failed operations\n- **Event-Driven Workflows**: Wait for specific time periods before checking conditions\n- **Batch Processing**: Add delays between batch operations to avoid overwhelming systems "
  },
  {
    "path": "cmd/samples/recipes/sleep/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\nconst (\n\tApplicationName   = \"sleepTaskList\"\n\tSleepWorkflowName = \"sleepWorkflow\"\n)\n\nfunc startWorkers(h *common.SampleHelper) {\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t\tFeatureFlags: client.FeatureFlags{\n\t\t\tWorkflowExecutionAlreadyCompletedErrorEnabled: true,\n\t\t},\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tsleepDuration := 30 * time.Second\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"sleep_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, SleepWorkflowName, sleepDuration)\n}\n\nfunc registerWorkflowAndActivity(h *common.SampleHelper) {\n\th.RegisterWorkflowWithAlias(sleepWorkflow, SleepWorkflowName)\n\th.RegisterActivity(mainSleepActivity)\n}\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\tregisterWorkflowAndActivity(&h)\n\t\tstartWorkers(&h)\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/sleep/sleep_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\nconst sleepWorkflowName = \"sleepWorkflow\"\n\n// sleepWorkflow demonstrates workflow.Sleep followed by a main activity call\nfunc sleepWorkflow(ctx workflow.Context, sleepDuration time.Duration) error {\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"Workflow started, will sleep\", zap.String(\"duration\", sleepDuration.String()))\n\n\t// Sleep for the specified duration\n\terr := workflow.Sleep(ctx, sleepDuration)\n\tif err != nil {\n\t\tlogger.Error(\"Sleep failed\", zap.Error(err))\n\t\treturn err\n\t}\n\n\tlogger.Info(\"Sleep finished, executing main activity\")\n\n\t// Set activity options\n\tactivityOptions := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, activityOptions)\n\n\tvar result string\n\terr = workflow.ExecuteActivity(ctx, mainSleepActivity).Get(ctx, &result)\n\tif err != nil {\n\t\tlogger.Error(\"Main activity failed\", zap.Error(err))\n\t\treturn err\n\t}\n\n\tlogger.Info(\"Workflow completed\", zap.String(\"Result\", result))\n\treturn nil\n}\n\n// mainSleepActivity is a simple activity for demonstration\nfunc mainSleepActivity(ctx context.Context) (string, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"mainSleepActivity executed\")\n\treturn \"Main sleep activity completed\", nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/sleep/sleep_workflow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\nfunc Test_Sleep(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\n\tenv := testSuite.NewTestWorkflowEnvironment()\n\tenv.RegisterWorkflow(sleepWorkflow)\n\tenv.RegisterActivity(mainSleepActivity)\n\n\tvar activityMessage string\n\tenv.SetOnActivityCompletedListener(func(activityInfo *activity.Info, result encoded.Value, err error) {\n\t\tresult.Get(&activityMessage)\n\t})\n\n\tsleepDuration := 5 * time.Second\n\tenv.ExecuteWorkflow(sleepWorkflow, sleepDuration)\n\n\trequire.True(t, env.IsWorkflowCompleted())\n\trequire.NoError(t, env.GetWorkflowError())\n\trequire.Equal(t, \"Main sleep activity completed\", activityMessage)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/splitmerge/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"splitmerge_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, sampleSplitMergeWorkflow, 5)\n}\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(sampleSplitMergeWorkflow)\n\t\th.RegisterActivity(chunkProcessingActivity)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/splitmerge/splitmerge_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n* This sample workflow demonstrates how to use multiple Cadence corotinues (instead of native goroutine) to process a\n* chunk of a large work item in parallel, and then merge the intermediate result to generate the final result.\n* In cadence workflow, you should not use go routine. Instead, you use corotinue via workflow.Go method.\n */\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"splitmergeGroup\"\n\ntype (\n\t// ChunkResult contains the result for this sample\n\tChunkResult struct {\n\t\tNumberOfItemsInChunk int\n\t\tSumInChunk           int\n\t}\n)\n\n// sampleSplitMergeWorkflow workflow decider\nfunc sampleSplitMergeWorkflow(ctx workflow.Context, workerCount int) (ChunkResult, error) {\n\tchunkResultChannel := workflow.NewChannel(ctx)\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tfor i := 1; i <= workerCount; i++ {\n\t\tchunkID := i\n\t\tworkflow.Go(ctx, func(ctx workflow.Context) {\n\t\t\tvar result ChunkResult\n\t\t\terr := workflow.ExecuteActivity(ctx, chunkProcessingActivity, chunkID).Get(ctx, &result)\n\t\t\tif err == nil {\n\t\t\t\tchunkResultChannel.Send(ctx, result)\n\t\t\t} else {\n\t\t\t\tchunkResultChannel.Send(ctx, err)\n\t\t\t}\n\t\t})\n\t}\n\n\tvar totalItemCount, totalSum int\n\tfor i := 1; i <= workerCount; i++ {\n\t\tvar v interface{}\n\t\tchunkResultChannel.Receive(ctx, &v)\n\t\tswitch r := v.(type) {\n\t\tcase error:\n\t\t// failed to process this chunk\n\t\t// some proper error handling code here\n\t\tcase ChunkResult:\n\t\t\ttotalItemCount += r.NumberOfItemsInChunk\n\t\t\ttotalSum += r.SumInChunk\n\t\t}\n\t}\n\n\tworkflow.GetLogger(ctx).Info(\"Workflow completed.\")\n\n\treturn ChunkResult{totalItemCount, totalSum}, nil\n}\n\nfunc chunkProcessingActivity(ctx context.Context, chunkID int) (result ChunkResult, err error) {\n\t// some fake processing logic here\n\tnumberOfItemsInChunk := chunkID\n\tsumInChunk := chunkID * chunkID\n\n\tactivity.GetLogger(ctx).Info(\"Chunck processed\", zap.Int(\"chunkID\", chunkID))\n\treturn ChunkResult{numberOfItemsInChunk, sumInChunk}, nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/splitmerge/splitmerge_workflow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/suite\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\ntype UnitTestSuite struct {\n\tsuite.Suite\n\ttestsuite.WorkflowTestSuite\n\n\tenv *testsuite.TestWorkflowEnvironment\n}\n\nfunc TestUnitTestSuite(t *testing.T) {\n\tsuite.Run(t, new(UnitTestSuite))\n}\n\nfunc (s *UnitTestSuite) SetupTest() {\n\ts.env = s.NewTestWorkflowEnvironment()\n\ts.env.RegisterWorkflow(sampleSplitMergeWorkflow)\n\ts.env.RegisterActivity(chunkProcessingActivity)\n}\n\nfunc (s *UnitTestSuite) TearDownTest() {\n\ts.env.AssertExpectations(s.T())\n}\n\nfunc (s *UnitTestSuite) Test_Workflow() {\n\tworkerCount := 5\n\n\ts.env.ExecuteWorkflow(sampleSplitMergeWorkflow, workerCount)\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n\n\tvar result ChunkResult\n\ts.env.GetWorkflowResult(&result)\n\n\ttotalItem, totalSum := 0, 0\n\tfor i := 1; i <= workerCount; i++ {\n\t\ttotalItem += i\n\t\ttotalSum += i * i\n\t}\n\n\ts.Equal(totalItem, result.NumberOfItemsInChunk)\n\ts.Equal(totalSum, result.SumInChunk)\n}\n"
  },
  {
    "path": "cmd/samples/recipes/timer/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t\tMaxConcurrentActivityExecutionSize: 3,\n\t}\n\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"timer_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, sampleTimerWorkflow, time.Second*3)\n}\n\nfunc main() {\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflow(sampleTimerWorkflow)\n\t\th.RegisterActivity(orderProcessingActivity)\n\t\th.RegisterActivity(sendEmailActivity)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/timer/workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"math/rand\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"timerGroup\"\n\n// sampleTimerWorkflow workflow decider\nfunc sampleTimerWorkflow(ctx workflow.Context, processingTimeThreshold time.Duration) error {\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tchildCtx, cancelHandler := workflow.WithCancel(ctx)\n\tselector := workflow.NewSelector(ctx)\n\n\t// In this sample case, we want to demo a use case where the workflow starts a long running order processing operation\n\t// and in the case that the processing takes too long, we want to send out a notification email to user about the delay,\n\t// but we won't cancel the operation. If the operation finishes before the timer fires, then we want to cancel the timer.\n\n\tvar processingDone bool\n\tf := workflow.ExecuteActivity(ctx, orderProcessingActivity)\n\tselector.AddFuture(f, func(f workflow.Future) {\n\t\tprocessingDone = true\n\t\t// cancel timerFuture\n\t\tcancelHandler()\n\t})\n\n\t// use timer future to send notification email if processing takes too long\n\ttimerFuture := workflow.NewTimer(childCtx, processingTimeThreshold)\n\tselector.AddFuture(timerFuture, func(f workflow.Future) {\n\t\tif !processingDone {\n\t\t\t// processing is not done yet when timer fires, send notification email\n\t\t\tworkflow.ExecuteActivity(ctx, sendEmailActivity).Get(ctx, nil)\n\t\t}\n\t})\n\n\t// wait the timer or the order processing to finish\n\tselector.Select(ctx)\n\n\t// now either the order processing is finished, or timer is fired.\n\tif !processingDone {\n\t\t// processing not done yet, so the handler for timer will send out notification email.\n\t\t// we still want the order processing to finish, so wait on it.\n\t\tselector.Select(ctx)\n\t}\n\n\tworkflow.GetLogger(ctx).Info(\"Workflow completed.\")\n\treturn nil\n}\n\nfunc orderProcessingActivity(ctx context.Context) error {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"sampleActivity processing started.\")\n\ttimeNeededToProcess := time.Second * time.Duration(rand.Intn(10))\n\ttime.Sleep(timeNeededToProcess)\n\tlogger.Info(\"sampleActivity done.\", zap.Duration(\"duration\", timeNeededToProcess))\n\treturn nil\n}\n\nfunc sendEmailActivity(ctx context.Context) error {\n\tactivity.GetLogger(ctx).Info(\"sendEmailActivity sending notification email as the process takes long time.\")\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/timer/workflow_test.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/mock\"\n\t\"github.com/stretchr/testify/suite\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\ntype UnitTestSuite struct {\n\tsuite.Suite\n\ttestsuite.WorkflowTestSuite\n\n\tenv *testsuite.TestWorkflowEnvironment\n}\n\nfunc TestUnitTestSuite(t *testing.T) {\n\tsuite.Run(t, new(UnitTestSuite))\n}\n\nfunc (s *UnitTestSuite) SetupTest() {\n\ts.env = s.NewTestWorkflowEnvironment()\n\ts.env.RegisterWorkflow(sampleTimerWorkflow)\n\ts.env.RegisterActivity(orderProcessingActivity)\n\ts.env.RegisterActivity(sendEmailActivity)\n}\n\nfunc (s *UnitTestSuite) Test_Workflow_FastProcessing() {\n\t// mock to return immediately to simulate fast processing case\n\ts.env.OnActivity(orderProcessingActivity, mock.Anything).Return(nil)\n\ts.env.OnActivity(sendEmailActivity, mock.Anything).Return(func(ctx context.Context) error {\n\t\t// in fast processing case, this method should not get called\n\t\ts.FailNow(\"sendEmailActivity should not get called\")\n\t\treturn nil\n\t})\n\n\ts.env.ExecuteWorkflow(sampleTimerWorkflow, time.Minute)\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n}\n\nfunc (s *UnitTestSuite) Test_Workflow_SlowProcessing() {\n\twg := &sync.WaitGroup{}\n\twg.Add(1)\n\ts.env.OnActivity(orderProcessingActivity, mock.Anything).Return(func(ctx context.Context) error {\n\t\t// simulate slow processing, will complete this activity only after the sendEmailActivity is called.\n\t\twg.Wait()\n\t\treturn nil\n\t})\n\ts.env.OnActivity(sendEmailActivity, mock.Anything).Return(func(ctx context.Context) error {\n\t\twg.Done()\n\t\treturn nil\n\t})\n\n\ts.env.ExecuteWorkflow(sampleTimerWorkflow, time.Microsecond)\n\ts.True(s.env.IsWorkflowCompleted())\n\ts.NoError(s.env.GetWorkflowError())\n\ts.env.AssertExpectations(s.T())\n}\n"
  },
  {
    "path": "cmd/samples/recipes/tracing/helloworld_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n/**\n * This is the tracing hello world workflow sample.\n */\n\n// ApplicationName is the task list for this sample\nconst ApplicationName = \"helloWorldGroup\"\n\nconst helloWorldWorkflowName = \"helloWorldWorkflow\"\n\n// helloWorkflow workflow decider\nfunc helloWorldWorkflow(ctx workflow.Context, name string) error {\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"helloworld workflow started\")\n\tvar helloworldResult string\n\terr := workflow.ExecuteActivity(ctx, helloWorldActivity, name).Get(ctx, &helloworldResult)\n\tif err != nil {\n\t\tlogger.Error(\"Activity failed.\", zap.Error(err))\n\t\treturn err\n\t}\n\n\tlogger.Info(\"Workflow completed.\", zap.String(\"Result\", helloworldResult))\n\n\treturn nil\n}\n\nfunc helloWorldActivity(ctx context.Context, name string) (string, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"helloworld activity started\")\n\treturn \"Hello \" + name + \"!\", nil\n}\n"
  },
  {
    "path": "cmd/samples/recipes/tracing/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"github.com/pborman/uuid\"\n\t\"github.com/uber/jaeger-client-go\"\n\t\"github.com/uber/jaeger-client-go/config\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t\tFeatureFlags: client.FeatureFlags{\n\t\t\tWorkflowExecutionAlreadyCompletedErrorEnabled: true,\n\t\t},\n\t\tTracer: h.Tracer,\n\t}\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              \"helloworld_\" + uuid.New(),\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Minute,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t}\n\th.StartWorkflow(workflowOptions, helloWorldWorkflowName, \"Cadence\")\n}\n\nfunc registerWorkflowAndActivity(\n\th *common.SampleHelper,\n) {\n\th.RegisterWorkflowWithAlias(helloWorldWorkflow, helloWorldWorkflowName)\n\th.RegisterActivity(helloWorldActivity)\n}\n\nfunc main() {\n\ttracer, closer := initJaeger(\"cadence-tracing-sample\")\n\tdefer closer.Close()\n\t\n\tvar mode string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker, trigger or shadower.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.Tracer = tracer\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\tregisterWorkflowAndActivity(&h)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\t}\n}\n\n// initJaeger returns an instance of Jaeger Tracer that samples 100% of traces and logs all spans to stdout.\nfunc initJaeger(service string) (opentracing.Tracer, io.Closer) {\n\tcfg := &config.Configuration{\n\t\tServiceName: service,\n\t\tSampler: &config.SamplerConfig{\n\t\t\tType:  \"const\",\n\t\t\tParam: 1,\n\t\t},\n\t\tReporter: &config.ReporterConfig{\n\t\t\tLogSpans: true,\n\t\t},\n\t}\n\ttracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger))\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"ERROR: cannot init Jaeger: %v\\n\", err))\n\t}\n\treturn tracer, closer\n}\n"
  },
  {
    "path": "cmd/samples/recipes/versioning/README.md",
    "content": "# Versioning Workflow Example\n\nThis example demonstrates how to safely deploy versioned workflows using Cadence's versioning APIs. It shows how to handle workflow evolution while maintaining backward compatibility and enabling safe rollbacks.\n\n## Overview\n\nThe versioning sample implements a workflow that evolves through multiple versions (V1 → V2 → V3 → V4) with rollbacks, demonstrating:\n\n- **Safe Deployment**: How to deploy new workflow versions without breaking existing executions\n- **Backward Compatibility**: How to handle workflows started with older versions\n- **Rollback Capability**: How to safely rollback to previous versions\n- **Version Isolation**: How different versions can execute different logic paths\n\n## Workflow Versions\n\n### Version 1 (V1)\n- Executes `FooActivity` only\n- Uses `workflow.DefaultVersion` for the change ID\n\n### Version 2 (V2) \n- Supports both `FooActivity` and `BarActivity`\n- Uses `workflow.GetVersion()` with `workflow.ExecuteWithMinVersion()` to handle both old and new workflows\n- Workflows started by V1 continue using `FooActivity`\n\n### Version 3 (V3)\n- Similar to V2 but uses standard `workflow.GetVersion()` (without `ExecuteWithMinVersion`)\n- All new workflows use version 1 of the change ID\n\n### Version 4 (V4)\n- Only supports `BarActivity`\n- Forces all workflows to use version 1 of the change ID\n- **Breaking change**: Cannot execute workflows started by V1\n\n## Key Cadence APIs Used\n\n- `workflow.GetVersion()`: Determines which version of code to execute\n- `workflow.ExecuteWithVersion()`: Executes code with a specific version\n- `workflow.ExecuteWithMinVersion()`: Executes code with minimum version requirement\n- `workflow.DefaultVersion`: Represents the original version before any changes\n\n## Safe Deployment Flow\n\nThis example demonstrates a safe deployment strategy that allows you to:\n\n1. **Deploy new versions** while keeping old workers running\n2. **Test compatibility** before fully switching over\n3. **Rollback safely** if issues are discovered\n4. **Gradually migrate** workflows to new versions\n\n\n## Important Notes\n\n- **Single Workflow Limitation**: This sample allows only one workflow at a time to simplify the signal handling mechanism. In production, you would typically handle multiple workflows.\n- **Signal Method**: The workflow uses a simple signal method to stop gracefully, keeping the implementation straightforward.\n- **Breaking Changes**: V4 demonstrates what happens when you introduce a breaking change - workflows started by V1 cannot be executed.\n\n## Version Compatibility Matrix\n\n| Started By | V1 Worker | V2 Worker | V3 Worker | V4 Worker |\n|------------|-----------|-----------|-----------|-----------|\n| V1         | ✅        | ✅        | ✅        | ❌        |\n| V2         | ❌        | ✅        | ✅        | ✅        |\n| V3         | ❌        | ✅        | ✅        | ✅        |\n| V4         | ❌        | ✅        | ✅        | ✅        |\n\n## Running the Example\n\n### Prerequisites\n\nMake sure you have Cadence server running and the sample compiled:\n\n```bash\n# Build the sample\ngo build -o bin/versioning cmd/samples/recipes/versioning/*.go\n```\n\n### Step-by-Step Deployment Simulation\n\n#### 1. Start Worker V1\n```bash\n./bin/versioning -m worker -v 1\n```\n\n#### 2. Trigger a Workflow\n```bash\n./bin/versioning -m trigger\n```\n\nWait for logs in the V1 worker to ensure that a workflow has been executed by worker V1.\n\n#### 3. Deploy Worker V2\nLet's simulate a deployment from V1 to V2 and run a V2 worker alongside the V1 worker:\n\n```bash\n./bin/versioning -m worker -v 2\n```\n\nThe workflow should still be executed by worker V1.\n\n#### 4. Test V2 Compatibility\nLet's simulate that worker V1 is shut down and the workflow will be rescheduled to the V2 worker:\n* Kill the process of worker V1 (Ctrl+C), then wait 5 seconds to see workflow rescheduling to worker V2 without errors.\n\nVerify logs of the V2 worker - it should handle the workflow started by V1.\n\n#### 5. Upgrade to Version V3\nLet's continue the deployment and upgrade to V3, running a V3 worker alongside the V2 worker:\n\n```bash\n./bin/versioning -m worker -v 3\n```\n\nThe workflow should still be executed by worker V2.\n\n#### 6. Test V3 Compatibility\nLet's simulate that worker V2 is shut down and the workflow will be rescheduled to the V3 worker:\n\n* Kill the process of worker V2, then wait 5 seconds to see workflow rescheduling to worker V3 without errors.\n\nVerify logs of the V3 worker - it should handle the workflow started by V2.\n\n#### 7. Gracefully Stop the Workflow\nBefore upgrading to V4, we should ensure that the workflow has been stopped, otherwise it will fail. For this, we need to send a signal to stop it gracefully:\n\n```bash\n./bin/versioning -m stop\n```\n\nYou should see that the workflow has been stopped.\n\n#### 8. Start a New Workflow\nLet's start a new workflow:\n\n```bash\n./bin/versioning -m trigger\n```\n\nThe workflow will use version 1 of the change ID (V3's and V4's default).\n\n#### 9. Rollback to Worker V2\nLet's imagine that V3 has an issue and we need to rollback to V2. Let's start a worker V2:\n\n```bash\n./bin/versioning -m worker -v 2\n```\n\n* Kill the process of worker V3, then wait for workflow rescheduling.\n* Verify logs of V2 worker - V2 worker should handle workflows started by V3.\n\n#### 10. Aggressive Upgrade: V2 to V4 (Breaking Change)\nWe decide to combine getting rid of support for V1 and make an upgrade straightforward to V4:\n\n```bash\n./bin/versioning -m worker -v 4\n```\n\n* Kill the process of worker V2, then wait for workflow rescheduling.\n* Verify logs of V4 worker - V4 worker should handle workflows started by V4.\n\n\n## Command Reference\n\n```bash\n# Start a worker with specific version\n./bin/versioning -m worker -v <version>\n\n# Start a new workflow\n./bin/versioning -m trigger\n\n# Stop the running workflow\n./bin/versioning -m stop\n```\n\nWhere `<version>` can be:\n- `1` or `v1` - Version 1 (FooActivity only, DefaultVersion)\n- `2` or `v2` - Version 2 (FooActivity + BarActivity, DefaultVersion)\n- `3` or `v3` - Version 3 (FooActivity + BarActivity, Version #1)\n- `4` or `v4` - Version 4 (BarActivity only, Version #1)\n"
  },
  {
    "path": "cmd/samples/recipes/versioning/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) worker.Worker {\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope:      h.WorkerMetricScope,\n\t\tLogger:            h.Logger,\n\t\tWorkerStopTimeout: 1 * time.Second,\n\t}\n\treturn h.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n}\n\nfunc startWorkflow(h *common.SampleHelper) {\n\t// Allow to run only one Versioned workflow at a time\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              VersionedWorkflowID,\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Hour,\n\t\tDecisionTaskStartToCloseTimeout: time.Minute,\n\t\tWorkflowIDReusePolicy:           client.WorkflowIDReusePolicyAllowDuplicate,\n\t}\n\th.StartWorkflow(workflowOptions, VersionedWorkflowName, 0)\n}\n\n// stopWorkflow sends a signal to the workflow to stop it gracefully.\nfunc stopWorkflow(h *common.SampleHelper) {\n\th.Logger.Info(\"Stopping workflow\")\n\th.SignalWorkflow(VersionedWorkflowID, StopSignalName, \"\")\n}\n\nfunc main() {\n\tvar mode string\n\tvar version string\n\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker (version flag is required), trigger (start a new workflow, only one allowed), stop (stop a running workflow). Default is trigger.\")\n\tflag.StringVar(&version, \"v\", \"\", \"Version of the workflow to run, supported versions are 1, 2, 3, or 4. Required in worker mode.\")\n\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\tswitch version {\n\t\tcase \"1\", \"v1\":\n\t\t\tSetupHelperForVersionedWorkflowV1(&h)\n\n\t\tcase \"2\", \"v2\":\n\t\t\tSetupHelperForVersionedWorkflowV2(&h)\n\n\t\tcase \"3\", \"v3\":\n\t\t\tSetupHelperForVersionedWorkflowV3(&h)\n\n\t\tcase \"4\", \"v4\":\n\t\t\tSetupHelperForVersionedWorkflowV4(&h)\n\n\t\tcase \"\":\n\t\t\tfmt.Printf(\"-v flag is required for worker mode. Use -v 1, -v 2, -v 3, or -v 4 to specify the version.\\n\")\n\t\t\tos.Exit(1)\n\n\t\tdefault:\n\t\t\tfmt.Printf(\"Invalid version specified:%s . Use -v 1, -v 2, -v 3, or -v 4.\", version)\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long-running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\n\tcase \"trigger\":\n\t\tstartWorkflow(&h)\n\n\tcase \"stop\":\n\t\tstopWorkflow(&h)\n\n\tdefault:\n\t\tfmt.Printf(\"Invalid mode specified: %s. Use -m worker, -m trigger, -m stop.\\n\", mode)\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recipes/versioning/versioned_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n\t\"time\"\n)\n\n/**\n * This sample workflow continuously counting signals and do continue as new\n */\n\nconst (\n\t// ApplicationName is the task list for this sample\n\tApplicationName = \"versioning\"\n\n\t// TestChangeID is a constant used to identify the version change in the workflow.\n\tTestChangeID = \"test-change\"\n\n\t// FooActivityName and BarActivityName are the names of the activities used in the workflows.\n\tFooActivityName = \"FooActivity\"\n\tBarActivityName = \"BarActivity\"\n\n\t// VersionedWorkflowName is the name of the versioned workflow.\n\tVersionedWorkflowName = \"VersionedWorkflow\"\n\n\t// VersionedWorkflowID is the ID of the versioned workflow.\n\tVersionedWorkflowID = \"versioned_workflow\"\n\n\t// StopSignalName is the name of the signal used to stop the workflow to finish it successfully\n\tStopSignalName = \"StopSignal\"\n)\n\nconst (\n\tV1 int32 = iota + 1\n\tV2\n\tV3\n\tV4\n)\n\nvar activityOptions = workflow.ActivityOptions{\n\tScheduleToStartTimeout: time.Minute,\n\tStartToCloseTimeout:    time.Minute,\n\tHeartbeatTimeout:       time.Second * 20,\n}\n\n// VersionedWorkflowV1 is the first version of the workflow, supports only DefaultVersion.\n// All workflows started by this version will have the change ID set to DefaultVersion.\nfunc VersionedWorkflowV1(ctx workflow.Context) error {\n\tctx = workflow.WithActivityOptions(ctx, activityOptions)\n\n\terr := workflow.ExecuteActivity(ctx, FooActivityName).Get(ctx, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn waitForSignal(ctx, V1)\n}\n\n// VersionedWorkflowV2 is the second version of the workflow, supports DefaultVersion and 1\n// All workflows started by this version will have the change ID set to DefaultVersion.\nfunc VersionedWorkflowV2(ctx workflow.Context) error {\n\tctx = workflow.WithActivityOptions(ctx, activityOptions)\n\n\tvar err error\n\tvar version workflow.Version\n\n\tversion = workflow.GetVersion(ctx, TestChangeID, workflow.DefaultVersion, 1, workflow.ExecuteWithMinVersion())\n\tif version == workflow.DefaultVersion {\n\t\terr = workflow.ExecuteActivity(ctx, FooActivityName).Get(ctx, nil)\n\t} else {\n\t\terr = workflow.ExecuteActivity(ctx, BarActivityName).Get(ctx, nil)\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn waitForSignal(ctx, V2)\n}\n\n// VersionedWorkflowV3 is the third version of the workflow, supports DefaultVersion and 1\n// All workflows started by this version will have the change ID set to 1.\nfunc VersionedWorkflowV3(ctx workflow.Context) error {\n\tctx = workflow.WithActivityOptions(ctx, activityOptions)\n\n\tvar err error\n\tvar version workflow.Version\n\n\tversion = workflow.GetVersion(ctx, TestChangeID, workflow.DefaultVersion, 1)\n\tif version == workflow.DefaultVersion {\n\t\terr = workflow.ExecuteActivity(ctx, FooActivityName).Get(ctx, nil)\n\t} else {\n\t\terr = workflow.ExecuteActivity(ctx, BarActivityName).Get(ctx, nil)\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn waitForSignal(ctx, V3)\n}\n\n// VersionedWorkflowV4 is the fourth version of the workflow, supports only version 1\n// All workflows started by this version will have the change ID set to 1.\nfunc VersionedWorkflowV4(ctx workflow.Context) error {\n\tctx = workflow.WithActivityOptions(ctx, activityOptions)\n\n\tworkflow.GetVersion(ctx, TestChangeID, 1, 1)\n\terr := workflow.ExecuteActivity(ctx, BarActivityName).Get(ctx, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn waitForSignal(ctx, V4)\n}\n\nfunc waitForSignal(ctx workflow.Context, version int32) error {\n\tworkflow.GetLogger(ctx).Info(\"Waiting for signal\", zap.Int32(\"Worker Version\", version))\n\n\tsignalCh := workflow.GetSignalChannel(ctx, StopSignalName)\n\n\tfor {\n\t\tvar signal string\n\t\tif signalCh.ReceiveAsync(&signal) {\n\t\t\tbreak\n\t\t}\n\n\t\tworkflow.GetLogger(ctx).Info(\"No signal received yet, continuing to wait...\", zap.Int32(\"Worker Version\", version))\n\t\tworkflow.Sleep(ctx, time.Second*5)\n\t}\n\n\tworkflow.GetLogger(ctx).Info(\"Got the signal, finishing the workflow\", zap.Int32(\"Worker Version\", version))\n\treturn nil\n}\n\n// SetupHelperForVersionedWorkflowV1 registers VersionedWorkflowV1 and FooActivity\nfunc SetupHelperForVersionedWorkflowV1(h *common.SampleHelper) {\n\th.RegisterWorkflowWithAlias(VersionedWorkflowV1, VersionedWorkflowName)\n\th.RegisterActivityWithAlias(FooActivity, FooActivityName)\n}\n\n// SetupHelperForVersionedWorkflowV2 registers VersionedWorkflowV2, FooActivity, and BarActivity\nfunc SetupHelperForVersionedWorkflowV2(h *common.SampleHelper) {\n\th.RegisterWorkflowWithAlias(VersionedWorkflowV2, VersionedWorkflowName)\n\th.RegisterActivityWithAlias(FooActivity, FooActivityName)\n\th.RegisterActivityWithAlias(BarActivity, BarActivityName)\n}\n\n// SetupHelperForVersionedWorkflowV3 registers VersionedWorkflowV3, FooActivity, and BarActivity\nfunc SetupHelperForVersionedWorkflowV3(h *common.SampleHelper) {\n\th.RegisterWorkflowWithAlias(VersionedWorkflowV3, VersionedWorkflowName)\n\th.RegisterActivityWithAlias(FooActivity, FooActivityName)\n\th.RegisterActivityWithAlias(BarActivity, BarActivityName)\n}\n\n// SetupHelperForVersionedWorkflowV4 registers VersionedWorkflowV4 and BarActivity\nfunc SetupHelperForVersionedWorkflowV4(h *common.SampleHelper) {\n\th.RegisterWorkflowWithAlias(VersionedWorkflowV4, VersionedWorkflowName)\n\th.RegisterActivityWithAlias(BarActivity, BarActivityName)\n}\n\n// FooActivity returns \"foo\" as a result of the activity execution.\nfunc FooActivity(ctx context.Context) (string, error) {\n\tactivity.GetLogger(ctx).Info(\"Executing FooActivity\")\n\treturn \"foo\", nil\n}\n\n// BarActivity returns \"bar\" as a result of the activity execution.\nfunc BarActivity(ctx context.Context) (string, error) {\n\tactivity.GetLogger(ctx).Info(\"Executing BarActivity\")\n\treturn \"bar\", nil\n}\n"
  },
  {
    "path": "cmd/samples/recovery/README.md",
    "content": "### Recovery Sample\nThis sample implements a RecoveryWorkflow which is designed to restart all TripWorkflow executions which are currently\noutstanding and replay all signals from previous run.  This is useful where a bad code change is rolled out which\ncauses workflows to get stuck or state is corrupted.\n\n### Steps to run this sample\n1) Run the following command to start worker\n```\n./bin/query -m worker\n```\n2) Run the following command to start trip workflow\n```\n./bin/recovery -m trigger -w UserA -wt main.TripWorkflow\n```\n3) Run the following command to query trip workflow\n```\n./bin/recovery -m query -w UserA\n```\n4) Run the following command to send signal to trip workflow\n```\n./bin/recovery -m signal -w UserA -s '{\"ID\": \"Trip1\", \"Total\": 10}'\n```\n4) Run the following command to start recovery workflow\n```\n./bin/recovery -m trigger -w UserB -wt recoveryworkflow -i '{\"Type\": \"TripWorkflow\", \"Concurrency\": 2}'\n```"
  },
  {
    "path": "cmd/samples/recovery/cache/cache.go",
    "content": "package cache\n\nimport \"time\"\n\n// A Cache is a generalized interface to a cache.  See cache.LRU for a specific\n// implementation (bounded cache with LRU eviction)\ntype Cache interface {\n\t// Get retrieves an element based on a key, returning nil if the element\n\t// does not exist\n\tGet(key string) interface{}\n\n\t// Put adds an element to the cache, returning the previous element\n\tPut(key string, value interface{}) interface{}\n\n\t// PutIfNotExist puts a value associated with a given key if it does not exist\n\tPutIfNotExist(key string, value interface{}) (interface{}, error)\n\n\t// Delete deletes an element in the cache\n\tDelete(key string)\n\n\t// Release decrements the ref count of a pinned element. If the ref count\n\t// drops to 0, the element can be evicted from the cache.\n\tRelease(key string)\n\n\t// Size returns the number of entries currently stored in the Cache\n\tSize() int\n}\n\n// Options control the behavior of the cache\ntype Options struct {\n\t// TTL controls the time-to-live for a given cache entry.  Cache entries that\n\t// are older than the TTL will not be returned\n\tTTL time.Duration\n\n\t// InitialCapacity controls the initial capacity of the cache\n\tInitialCapacity int\n\n\t// Pin prevents in-use objects from getting evicted\n\tPin bool\n\n\t// RemovedFunc is an optional function called when an element\n\t// is scheduled for deletion\n\tRemovedFunc RemovedFunc\n}\n\n// RemovedFunc is a type for notifying applications when an item is\n// scheduled for removal from the Cache. If f is a function with the\n// appropriate signature and i is the interface{} scheduled for\n// deletion, Cache calls go f(i)\ntype RemovedFunc func(interface{})\n"
  },
  {
    "path": "cmd/samples/recovery/cache/lru.go",
    "content": "package cache\n\nimport (\n\t\"container/list\"\n\t\"errors\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar (\n\t// ErrCacheFull is returned if Put fails due to cache being filled with pinned elements\n\tErrCacheFull = errors.New(\"Cache capacity is fully occupied with pinned elements\")\n)\n\n// lru is a concurrent fixed size cache that evicts elements in lru order\ntype lru struct {\n\tmut      sync.Mutex\n\tbyAccess *list.List\n\tbyKey    map[string]*list.Element\n\tmaxSize  int\n\tttl      time.Duration\n\tpin      bool\n\trmFunc   RemovedFunc\n}\n\n// New creates a new cache with the given options\nfunc New(maxSize int, opts *Options) Cache {\n\tif opts == nil {\n\t\topts = &Options{}\n\t}\n\n\treturn &lru{\n\t\tbyAccess: list.New(),\n\t\tbyKey:    make(map[string]*list.Element, opts.InitialCapacity),\n\t\tttl:      opts.TTL,\n\t\tmaxSize:  maxSize,\n\t\tpin:      opts.Pin,\n\t\trmFunc:   opts.RemovedFunc,\n\t}\n}\n\n// NewLRU creates a new LRU cache of the given size, setting initial capacity\n// to the max size\nfunc NewLRU(maxSize int) Cache {\n\treturn New(maxSize, nil)\n}\n\n// NewLRUWithInitialCapacity creates a new LRU cache with an initial capacity\n// and a max size\nfunc NewLRUWithInitialCapacity(initialCapacity, maxSize int) Cache {\n\treturn New(maxSize, &Options{\n\t\tInitialCapacity: initialCapacity,\n\t})\n}\n\n// Get retrieves the value stored under the given key\nfunc (c *lru) Get(key string) interface{} {\n\tc.mut.Lock()\n\tdefer c.mut.Unlock()\n\n\telt := c.byKey[key]\n\tif elt == nil {\n\t\treturn nil\n\t}\n\n\tcacheEntry := elt.Value.(*cacheEntry)\n\n\tif c.pin {\n\t\tcacheEntry.refCount++\n\t}\n\n\tif cacheEntry.refCount == 0 && !cacheEntry.expiration.IsZero() && time.Now().After(cacheEntry.expiration) {\n\t\t// Entry has expired\n\t\tif c.rmFunc != nil {\n\t\t\tgo c.rmFunc(cacheEntry.value)\n\t\t}\n\t\tc.byAccess.Remove(elt)\n\t\tdelete(c.byKey, cacheEntry.key)\n\t\treturn nil\n\t}\n\n\tc.byAccess.MoveToFront(elt)\n\treturn cacheEntry.value\n}\n\n// Put puts a new value associated with a given key, returning the existing value (if present)\nfunc (c *lru) Put(key string, value interface{}) interface{} {\n\tif c.pin {\n\t\tpanic(\"Cannot use Put API in Pin mode. Use Delete and PutIfNotExist if necessary\")\n\t}\n\tval, _ := c.putInternal(key, value, true)\n\treturn val\n}\n\n// PutIfNotExist puts a value associated with a given key if it does not exist\nfunc (c *lru) PutIfNotExist(key string, value interface{}) (interface{}, error) {\n\texisting, err := c.putInternal(key, value, false)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif existing == nil {\n\t\t// This is a new value\n\t\treturn value, err\n\t}\n\n\treturn existing, err\n}\n\n// Delete deletes a key, value pair associated with a key\nfunc (c *lru) Delete(key string) {\n\tc.mut.Lock()\n\tdefer c.mut.Unlock()\n\n\telt := c.byKey[key]\n\tif elt != nil {\n\t\tentry := c.byAccess.Remove(elt).(*cacheEntry)\n\t\tif c.rmFunc != nil {\n\t\t\tgo c.rmFunc(entry.value)\n\t\t}\n\t\tdelete(c.byKey, key)\n\t}\n}\n\n// Release decrements the ref count of a pinned element.\nfunc (c *lru) Release(key string) {\n\tc.mut.Lock()\n\tdefer c.mut.Unlock()\n\n\telt := c.byKey[key]\n\tcacheEntry := elt.Value.(*cacheEntry)\n\tcacheEntry.refCount--\n}\n\n// Size returns the number of entries currently in the lru, useful if cache is not full\nfunc (c *lru) Size() int {\n\tc.mut.Lock()\n\tdefer c.mut.Unlock()\n\n\treturn len(c.byKey)\n}\n\n// Put puts a new value associated with a given key, returning the existing value (if present)\n// allowUpdate flag is used to control overwrite behavior if the value exists\nfunc (c *lru) putInternal(key string, value interface{}, allowUpdate bool) (interface{}, error) {\n\tc.mut.Lock()\n\tdefer c.mut.Unlock()\n\n\telt := c.byKey[key]\n\tif elt != nil {\n\t\tentry := elt.Value.(*cacheEntry)\n\t\texisting := entry.value\n\t\tif allowUpdate {\n\t\t\tentry.value = value\n\t\t}\n\t\tif c.ttl != 0 {\n\t\t\tentry.expiration = time.Now().Add(c.ttl)\n\t\t}\n\t\tc.byAccess.MoveToFront(elt)\n\t\tif c.pin {\n\t\t\tentry.refCount++\n\t\t}\n\t\treturn existing, nil\n\t}\n\n\tentry := &cacheEntry{\n\t\tkey:   key,\n\t\tvalue: value,\n\t}\n\n\tif c.pin {\n\t\tentry.refCount++\n\t}\n\n\tif c.ttl != 0 {\n\t\tentry.expiration = time.Now().Add(c.ttl)\n\t}\n\n\tc.byKey[key] = c.byAccess.PushFront(entry)\n\tif len(c.byKey) == c.maxSize {\n\t\toldest := c.byAccess.Back().Value.(*cacheEntry)\n\n\t\tif oldest.refCount > 0 {\n\t\t\t// Cache is full with pinned elements\n\t\t\t// revert the insert and return\n\t\t\tc.byAccess.Remove(c.byAccess.Front())\n\t\t\tdelete(c.byKey, key)\n\t\t\treturn nil, ErrCacheFull\n\t\t}\n\n\t\tc.byAccess.Remove(c.byAccess.Back())\n\t\tif c.rmFunc != nil {\n\t\t\tgo c.rmFunc(oldest.value)\n\t\t}\n\t\tdelete(c.byKey, oldest.key)\n\t}\n\n\treturn nil, nil\n}\n\ntype cacheEntry struct {\n\tkey        string\n\texpiration time.Time\n\tvalue      interface{}\n\trefCount   int\n}\n"
  },
  {
    "path": "cmd/samples/recovery/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/zap\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n\t\"github.com/uber-common/cadence-samples/cmd/samples/recovery/cache\"\n)\n\n// This needs to be done as part of a bootstrap step when the process starts.\n// The workers are supposed to be long running.\nfunc startWorkers(h *common.SampleHelper) {\n\tworkflowClient, err := h.Builder.BuildCadenceClient()\n\tif err != nil {\n\t\th.Logger.Error(\"Failed to build cadence client.\", zap.Error(err))\n\t\tpanic(err)\n\t}\n\tctx := context.WithValue(context.Background(), CadenceClientKey, workflowClient)\n\tctx = context.WithValue(ctx, WorkflowExecutionCacheKey, cache.NewLRU(10))\n\n\t// Configure worker options.\n\tworkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t\tBackgroundActivityContext: ctx,\n\t}\n\n\th.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)\n\n\t// Configure worker options.\n\thostSpecificWorkerOptions := worker.Options{\n\t\tMetricsScope: h.WorkerMetricScope,\n\t\tLogger:       h.Logger,\n\t\tBackgroundActivityContext: ctx,\n\t\tDisableWorkflowWorker: true,\n\t}\n\n\th.StartWorkers(h.Config.DomainName, HostID, hostSpecificWorkerOptions)\n}\n\nfunc startTripWorkflow(h *common.SampleHelper, workflowID string, user UserState) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              workflowID,\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Hour * 24,\n\t\tDecisionTaskStartToCloseTimeout: time.Second * 10,\n\t}\n\th.StartWorkflow(workflowOptions, tripWorkflow, user)\n}\n\nfunc startRecoveryWorkflow(h *common.SampleHelper, workflowID string, params Params) {\n\tworkflowOptions := client.StartWorkflowOptions{\n\t\tID:                              workflowID,\n\t\tTaskList:                        ApplicationName,\n\t\tExecutionStartToCloseTimeout:    time.Hour * 24,\n\t\tDecisionTaskStartToCloseTimeout: time.Second * 10,\n\t}\n\th.StartWorkflow(workflowOptions, recoverWorkflow, params)\n}\n\nfunc main() {\n\tvar mode, workflowID,signal, input, workflowType string\n\tflag.StringVar(&mode, \"m\", \"trigger\", \"Mode is worker or trigger.\")\n\tflag.StringVar(&workflowID, \"w\", \"workflow_A\", \"WorkflowID\")\n\tflag.StringVar(&signal, \"s\", \"signal_data\", \"SignalData\")\n\tflag.StringVar(&input, \"i\", \"{}\", \"Workflow input parameters.\")\n\tflag.StringVar(&workflowType, \"wt\", \"main.tripWorkflow\", \"Workflow type.\")\n\tflag.Parse()\n\n\tvar h common.SampleHelper\n\th.SetupServiceConfig()\n\n\tswitch mode {\n\tcase \"worker\":\n\t\th.RegisterWorkflowWithAlias(recoverWorkflow, \"recoverWorkflow\")\n\t\th.RegisterWorkflowWithAlias(tripWorkflow, \"tripWorkflow\")\n\t\th.RegisterActivity(listOpenExecutions)\n\t\th.RegisterActivity(recoverExecutions)\n\t\tstartWorkers(&h)\n\n\t\t// The workers are supposed to be long running process that should not exit.\n\t\t// Use select{} to block indefinitely for samples, you can quit by CMD+C.\n\t\tselect {}\n\tcase \"trigger\":\n\t\tswitch workflowType {\n\t\tcase \"tripworkflow\":\n\t\t\tvar userState UserState\n\t\t\tif err := json.Unmarshal([]byte(input), &userState); err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t\tstartTripWorkflow(&h, workflowID, userState)\n\t\tcase \"recoveryworkflow\":\n\t\t\tvar params Params\n\t\t\tif err := json.Unmarshal([]byte(input), &params); err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t\tstartRecoveryWorkflow(&h, workflowID, params)\n\t\t}\n\tcase \"query\":\n\t\th.QueryWorkflow(workflowID, \"\", QueryName)\n\tcase \"signal\":\n\t\tvar tripEvent TripEvent\n\t\tif err := json.Unmarshal([]byte(signal), &tripEvent); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\th.SignalWorkflow(workflowID, TripSignalName, tripEvent)\n\t}\n}\n"
  },
  {
    "path": "cmd/samples/recovery/recovery_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/pborman/uuid\"\n\t\"go.uber.org/cadence\"\n\t\"go.uber.org/cadence/.gen/go/shared\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/client\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n\n\t\"github.com/uber-common/cadence-samples/cmd/samples/common\"\n\t\"github.com/uber-common/cadence-samples/cmd/samples/recovery/cache\"\n)\n\ntype (\n\t// Params is the input parameters to RecoveryWorkflow\n\tParams struct {\n\t\tID          string\n\t\tType        string\n\t\tConcurrency int\n\t}\n\n\t// ListOpenExecutionsResult is the result returned from listOpenExecutions activity\n\tListOpenExecutionsResult struct {\n\t\tID     string\n\t\tCount  int\n\t\tHostID string\n\t}\n\n\t// RestartParams are parameters extracted from StartWorkflowExecution history event\n\tRestartParams struct {\n\t\tOptions client.StartWorkflowOptions\n\t\tState   UserState\n\t}\n\n\t// SignalParams are the parameters extracted from SignalWorkflowExecution history event\n\tSignalParams struct {\n\t\tName string\n\t\tData TripEvent\n\t}\n)\n\n// ClientKey is the key for lookup\ntype ClientKey int\n\nconst (\n\t// DomainName used for this sample\n\tDomainName = \"default\"\n\n\t// CadenceClientKey for retrieving cadence client from context\n\tCadenceClientKey ClientKey = iota\n\t// WorkflowExecutionCacheKey for retrieving executions cache from context\n\tWorkflowExecutionCacheKey\n)\n\n// HostID - Use a new uuid just for demo so we can run 2 host specific activity workers on same machine.\n// In real world case, you would use a hostname or ip address as HostID.\nvar HostID = uuid.New()\n\nvar (\n\t// ErrCadenceClientNotFound when cadence client is not found on context\n\tErrCadenceClientNotFound = errors.New(\"failed to retrieve cadence client from context\")\n\t// ErrExecutionCacheNotFound when executions cache is not found on context\n\tErrExecutionCacheNotFound = errors.New(\"failed to retrieve cache from context\")\n)\n\n// This is registration process where you register all your workflows\n// and activity function handlers.\nfunc init() {\n\tworkflow.RegisterWithOptions(recoverWorkflow, workflow.RegisterOptions{Name: \"recoverWorkflow\"})\n\tactivity.Register(listOpenExecutions)\n\tactivity.Register(recoverExecutions)\n}\n\n// recoverWorkflow is the workflow implementation to recover TripWorkflow executions\nfunc recoverWorkflow(ctx workflow.Context, params Params) error {\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"Recover workflow started.\")\n\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: 10 * time.Minute,\n\t\tStartToCloseTimeout:    10 * time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 30,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tvar result ListOpenExecutionsResult\n\terr := workflow.ExecuteActivity(ctx, listOpenExecutions, params.Type).Get(ctx, &result)\n\tif err != nil {\n\t\tlogger.Error(\"Failed to list open workflow executions.\", zap.Error(err))\n\t\treturn err\n\t}\n\n\tconcurrency := 1\n\tif params.Concurrency > 0 {\n\t\tconcurrency = params.Concurrency\n\t}\n\n\tif result.Count < concurrency {\n\t\tconcurrency = result.Count\n\t}\n\n\tbatchSize := result.Count / concurrency\n\tif result.Count%concurrency != 0 {\n\t\tbatchSize++\n\t}\n\n\t// Setup retry policy for recovery activity\n\tinfo := workflow.GetInfo(ctx)\n\texpiration := time.Duration(info.ExecutionStartToCloseTimeoutSeconds) * time.Second\n\tretryPolicy := &cadence.RetryPolicy{\n\t\tInitialInterval:    time.Second,\n\t\tBackoffCoefficient: 2,\n\t\tMaximumInterval:    10 * time.Second,\n\t\tExpirationInterval: expiration,\n\t\tMaximumAttempts:    100,\n\t}\n\tao = workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: expiration,\n\t\tStartToCloseTimeout:    expiration,\n\t\tHeartbeatTimeout:       time.Second * 30,\n\t\tRetryPolicy:            retryPolicy,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tdoneCh := workflow.NewChannel(ctx)\n\tfor i := 0; i < concurrency; i++ {\n\t\tstartIndex := i * batchSize\n\n\t\tworkflow.Go(ctx, func(ctx workflow.Context) {\n\t\t\terr = workflow.ExecuteActivity(ctx, recoverExecutions, result.ID, startIndex, batchSize).Get(ctx, nil)\n\t\t\tif err != nil {\n\t\t\t\tlogger.Error(\"Recover executions failed.\", zap.Int(\"StartIndex\", startIndex), zap.Error(err))\n\t\t\t} else {\n\t\t\t\tlogger.Info(\"Recover executions completed.\", zap.Int(\"StartIndex\", startIndex))\n\t\t\t}\n\n\t\t\tdoneCh.Send(ctx, \"done\")\n\t\t})\n\t}\n\n\tfor i := 0; i < concurrency; i++ {\n\t\tdoneCh.Receive(ctx, nil)\n\t}\n\n\tlogger.Info(\"Workflow completed.\", zap.Int(\"Result\", result.Count))\n\n\treturn nil\n}\n\nfunc listOpenExecutions(ctx context.Context, workflowType string) (*ListOpenExecutionsResult, error) {\n\tkey := uuid.New()\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"List all open executions of type.\",\n\t\tzap.String(\"WorkflowType\", workflowType),\n\t\tzap.String(\"HostID\", HostID))\n\n\tcadenceClient, err := getCadenceClientFromContext(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\texecutionsCache := ctx.Value(WorkflowExecutionCacheKey).(cache.Cache)\n\tif executionsCache == nil {\n\t\tlogger.Error(\"Could not retrieve cache from context.\")\n\t\treturn nil, ErrExecutionCacheNotFound\n\t}\n\n\topenExecutions, err := getAllExecutionsOfType(ctx, cadenceClient, workflowType)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\texecutionsCache.Put(key, openExecutions)\n\treturn &ListOpenExecutionsResult{\n\t\tID:     key,\n\t\tCount:  len(openExecutions),\n\t\tHostID: HostID,\n\t}, nil\n}\n\nfunc recoverExecutions(ctx context.Context, key string, startIndex, batchSize int) error {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"Starting execution recovery.\",\n\t\tzap.String(\"HostID\", HostID),\n\t\tzap.String(\"Key\", key),\n\t\tzap.Int(\"StartIndex\", startIndex),\n\t\tzap.Int(\"BatchSize\", batchSize))\n\n\texecutionsCache := ctx.Value(WorkflowExecutionCacheKey).(cache.Cache)\n\tif executionsCache == nil {\n\t\tlogger.Error(\"Could not retrieve cache from context.\")\n\t\treturn ErrExecutionCacheNotFound\n\t}\n\n\topenExecutions := executionsCache.Get(key).([]*shared.WorkflowExecution)\n\tendIndex := startIndex + batchSize\n\n\t// Check if this activity has previous heartbeat to retrieve progress from it\n\tif activity.HasHeartbeatDetails(ctx) {\n\t\tvar finishedIndex int\n\t\tif err := activity.GetHeartbeatDetails(ctx, &finishedIndex); err == nil {\n\t\t\t// we have finished progress\n\t\t\tstartIndex = finishedIndex + 1 // start from next one.\n\t\t}\n\t}\n\n\tfor index := startIndex; index < endIndex && index < len(openExecutions); index++ {\n\t\texecution := openExecutions[index]\n\t\tif err := recoverSingleExecution(ctx, execution.GetWorkflowId()); err != nil {\n\t\t\tlogger.Error(\"Failed to recover execution.\",\n\t\t\t\tzap.String(\"WorkflowID\", execution.GetWorkflowId()),\n\t\t\t\tzap.Error(err))\n\t\t\treturn err\n\t\t}\n\n\t\t// Record a heartbeat after each recovery of execution\n\t\tactivity.RecordHeartbeat(ctx, index)\n\t}\n\n\treturn nil\n}\n\nfunc recoverSingleExecution(ctx context.Context, workflowID string) error {\n\tlogger := activity.GetLogger(ctx)\n\tcadenceClient, err := getCadenceClientFromContext(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\texecution := &shared.WorkflowExecution{\n\t\tWorkflowId: common.StringPtr(workflowID),\n\t}\n\thistory, err := getHistory(ctx, execution)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif history == nil || len(history) == 0 {\n\t\t// Nothing to recover\n\t\treturn nil\n\t}\n\n\tfirstEvent := history[0]\n\tlastEvent := history[len(history)-1]\n\n\t// Extract information from StartWorkflowExecution parameters so we can start a new run\n\tparams, err := extractStateFromEvent(workflowID, firstEvent)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Parse the entire history and extract all signals so they can be replayed back to new run\n\tsignals, err := extractSignals(history)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// First terminate existing run if already running\n\tif !isExecutionCompleted(lastEvent) {\n\t\terr := cadenceClient.TerminateWorkflow(ctx, execution.GetWorkflowId(), execution.GetRunId(), \"Recover\", nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// Start new execution run\n\tnewRun, err := cadenceClient.StartWorkflow(ctx, params.Options, \"TripWorkflow\", params.State)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// re-inject all signals to new run\n\tfor _, s := range signals {\n\t\tcadenceClient.SignalWorkflow(ctx, execution.GetWorkflowId(), newRun.RunID, s.Name, s.Data)\n\t}\n\n\tlogger.Info(\"Successfully restarted workflow.\",\n\t\tzap.String(\"WorkflowID\", execution.GetWorkflowId()),\n\t\tzap.String(\"NewRunID\", newRun.RunID))\n\n\treturn nil\n}\n\nfunc extractStateFromEvent(workflowID string, event *shared.HistoryEvent) (*RestartParams, error) {\n\tswitch event.GetEventType() {\n\tcase shared.EventTypeWorkflowExecutionStarted:\n\t\tattr := event.WorkflowExecutionStartedEventAttributes\n\t\tstate, err := deserializeUserState(attr.Input)\n\t\tif err != nil {\n\t\t\t// Corrupted Workflow Execution State\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &RestartParams{\n\t\t\tOptions: client.StartWorkflowOptions{\n\t\t\t\tID:                              workflowID,\n\t\t\t\tTaskList:                        attr.TaskList.GetName(),\n\t\t\t\tExecutionStartToCloseTimeout:    time.Second * time.Duration(attr.GetExecutionStartToCloseTimeoutSeconds()),\n\t\t\t\tDecisionTaskStartToCloseTimeout: time.Second * time.Duration(attr.GetTaskStartToCloseTimeoutSeconds()),\n\t\t\t\tWorkflowIDReusePolicy:           client.WorkflowIDReusePolicyAllowDuplicate,\n\t\t\t\t//RetryPolicy: attr.RetryPolicy,\n\t\t\t},\n\t\t\tState: state,\n\t\t}, nil\n\tdefault:\n\t\treturn nil, errors.New(\"Unknown event type\")\n\t}\n}\n\nfunc extractSignals(events []*shared.HistoryEvent) ([]*SignalParams, error) {\n\tvar signals []*SignalParams\n\tfor _, event := range events {\n\t\tif event.GetEventType() == shared.EventTypeWorkflowExecutionSignaled {\n\t\t\tattr := event.WorkflowExecutionSignaledEventAttributes\n\t\t\tif attr.GetSignalName() == TripSignalName && attr.Input != nil && len(attr.Input) > 0 {\n\t\t\t\tsignalData, err := deserializeTripEvent(attr.Input)\n\t\t\t\tif err != nil {\n\t\t\t\t\t// Corrupted Signal Payload\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\tsignal := &SignalParams{\n\t\t\t\t\tName: attr.GetSignalName(),\n\t\t\t\t\tData: signalData,\n\t\t\t\t}\n\t\t\t\tsignals = append(signals, signal)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn signals, nil\n}\n\nfunc isExecutionCompleted(event *shared.HistoryEvent) bool {\n\tswitch event.GetEventType() {\n\tcase shared.EventTypeWorkflowExecutionCompleted, shared.EventTypeWorkflowExecutionTerminated,\n\t\tshared.EventTypeWorkflowExecutionCanceled, shared.EventTypeWorkflowExecutionFailed,\n\t\tshared.EventTypeWorkflowExecutionTimedOut:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc getAllExecutionsOfType(ctx context.Context, cadenceClient client.Client,\n\tworkflowType string) ([]*shared.WorkflowExecution, error) {\n\tvar openExecutions []*shared.WorkflowExecution\n\tvar nextPageToken []byte\n\tfor hasMore := true; hasMore; hasMore = len(nextPageToken) > 0 {\n\t\tresp, err := cadenceClient.ListOpenWorkflow(ctx, &shared.ListOpenWorkflowExecutionsRequest{\n\t\t\tDomain:          common.StringPtr(DomainName),\n\t\t\tMaximumPageSize: common.Int32Ptr(10),\n\t\t\tNextPageToken:   nextPageToken,\n\t\t\tStartTimeFilter: &shared.StartTimeFilter{\n\t\t\t\tEarliestTime: common.Int64Ptr(0),\n\t\t\t\tLatestTime:   common.Int64Ptr(time.Now().UnixNano()),\n\t\t\t},\n\t\t\tTypeFilter: &shared.WorkflowTypeFilter{\n\t\t\t\tName: common.StringPtr(workflowType),\n\t\t\t},\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfor _, r := range resp.Executions {\n\t\t\topenExecutions = append(openExecutions, r.Execution)\n\t\t}\n\n\t\tnextPageToken = resp.NextPageToken\n\t\tactivity.RecordHeartbeat(ctx, nextPageToken)\n\t}\n\n\treturn openExecutions, nil\n}\n\nfunc getHistory(ctx context.Context, execution *shared.WorkflowExecution) ([]*shared.HistoryEvent, error) {\n\tcadenceClient, err := getCadenceClientFromContext(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\titer := cadenceClient.GetWorkflowHistory(ctx, execution.GetWorkflowId(), execution.GetRunId(), false,\n\t\tshared.HistoryEventFilterTypeAllEvent)\n\tvar events []*shared.HistoryEvent\n\tfor iter.HasNext() {\n\t\tevent, err := iter.Next()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tevents = append(events, event)\n\t}\n\n\treturn events, nil\n}\n\nfunc getCadenceClientFromContext(ctx context.Context) (client.Client, error) {\n\tlogger := activity.GetLogger(ctx)\n\tcadenceClient := ctx.Value(CadenceClientKey).(client.Client)\n\tif cadenceClient == nil {\n\t\tlogger.Error(\"Could not retrieve cadence client from context.\")\n\t\treturn nil, ErrCadenceClientNotFound\n\t}\n\n\treturn cadenceClient, nil\n}\n"
  },
  {
    "path": "cmd/samples/recovery/trip_workflow.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\ntype (\n\t// UserState kept within workflow and passed from one run to another on ContinueAsNew\n\tUserState struct {\n\t\tTripCounter int\n\t}\n\n\t// TripEvent passed in as signal to TripWorkflow\n\tTripEvent struct {\n\t\tID    string\n\t\tTotal int\n\t}\n)\n\nconst (\n\t// TripSignalName is the signal name for trip completion event\n\tTripSignalName = \"trip_event\"\n\n\t// ApplicationName is the task list for this sample\n\tApplicationName = \"recoveryGroup\"\n\n\t// QueryName is the query type name\n\tQueryName = \"counter\"\n)\n\n// tripWorkflow to keep track of total trip count for a user\n// It waits on a TripEvent signal and increments a counter on each signal received by this workflow\n// Trip count is managed as workflow state and passed to new run after 10 signals received by each execution\nfunc tripWorkflow(ctx workflow.Context, state UserState) error {\n\tlogger := workflow.GetLogger(ctx)\n\tworkflowID := workflow.GetInfo(ctx).WorkflowExecution.ID\n\tlogger.Info(\"Trip Workflow Started for User.\",\n\t\tzap.String(\"User\", workflowID),\n\t\tzap.Int(\"TripCounter\", state.TripCounter))\n\n\t// Register query handler to return trip count\n\terr := workflow.SetQueryHandler(ctx, QueryName, func(input []byte) (int, error) {\n\t\treturn state.TripCounter, nil\n\t})\n\n\tif err != nil {\n\t\tlogger.Info(\"SetQueryHandler failed.\", zap.Error(err))\n\t\treturn err\n\t}\n\n\t// TripCh to wait on trip completed event signals\n\ttripCh := workflow.GetSignalChannel(ctx, TripSignalName)\n\tfor i := 0; i < 10; i++ {\n\t\tvar trip TripEvent\n\t\ttripCh.Receive(ctx, &trip)\n\t\tlogger.Info(\"Trip complete event received.\", zap.String(\"ID\", trip.ID), zap.Int(\"Total\", trip.Total))\n\t\tstate.TripCounter++\n\t}\n\n\tlogger.Info(\"Starting a new run.\", zap.Int(\"TripCounter\", state.TripCounter))\n\treturn workflow.NewContinueAsNewError(ctx, \"TripWorkflow\", state)\n}\n\nfunc deserializeUserState(data []byte) (UserState, error) {\n\tvar state UserState\n\tif err := json.Unmarshal(data, &state); err != nil {\n\t\treturn UserState{}, err\n\t}\n\n\treturn state, nil\n}\n\nfunc deserializeTripEvent(data []byte) (TripEvent, error) {\n\tvar trip TripEvent\n\tif err := json.Unmarshal(data, &trip); err != nil {\n\t\treturn TripEvent{}, err\n\t}\n\n\treturn trip, nil\n}\n"
  },
  {
    "path": "config/development.yaml",
    "content": "# config for sample\ndomain: \"cadence-samples\"\nservice: \"cadence-frontend\"\nhost: \"localhost:7833\"\n# config for emitting metrics\n#prometheus:\n#  listenAddress: \"127.0.0.1:9098\"\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/uber-common/cadence-samples\n\ngo 1.21\n\ntoolchain go1.24.2\n\nrequire (\n\tgithub.com/google/uuid v1.3.0\n\tgithub.com/m3db/prometheus_client_golang v0.8.1\n\tgithub.com/opentracing/opentracing-go v1.2.0\n\tgithub.com/pborman/uuid v1.2.1\n\tgithub.com/pkg/errors v0.9.1\n\tgithub.com/stretchr/testify v1.9.0\n\tgithub.com/uber-go/tally v3.4.3+incompatible\n\tgithub.com/uber/cadence-idl v0.0.0-20250616185004-cc6f52f87bc6\n\tgithub.com/uber/jaeger-client-go v2.30.0+incompatible\n\tgo.uber.org/cadence v1.3.1-rc.11\n\tgo.uber.org/yarpc v1.60.0\n\tgo.uber.org/zap v1.23.0\n\tgopkg.in/yaml.v2 v2.4.0\n\tgopkg.in/yaml.v3 v3.0.1\n)\n\nrequire (\n\tgithub.com/BurntSushi/toml v0.4.1 // indirect\n\tgithub.com/apache/thrift v0.16.0 // indirect\n\tgithub.com/benbjohnson/clock v1.3.0 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.1.1 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect\n\tgithub.com/gogo/googleapis v1.3.2 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/gogo/status v1.1.0 // indirect\n\tgithub.com/golang-jwt/jwt/v5 v5.2.0 // indirect\n\tgithub.com/golang/mock v1.5.0 // indirect\n\tgithub.com/golang/protobuf v1.5.3 // indirect\n\tgithub.com/jonboulle/clockwork v0.4.0 // indirect\n\tgithub.com/m3db/prometheus_client_model v0.1.0 // indirect\n\tgithub.com/m3db/prometheus_common v0.1.0 // indirect\n\tgithub.com/m3db/prometheus_procfs v0.8.1 // indirect\n\tgithub.com/marusama/semaphore/v2 v2.5.0 // indirect\n\tgithub.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/prometheus/client_golang v1.11.1 // indirect\n\tgithub.com/prometheus/client_model v0.2.0 // indirect\n\tgithub.com/prometheus/common v0.26.0 // indirect\n\tgithub.com/prometheus/procfs v0.6.0 // indirect\n\tgithub.com/robfig/cron v1.2.0 // indirect\n\tgithub.com/stretchr/objx v0.5.2 // indirect\n\tgithub.com/twmb/murmur3 v1.1.6 // indirect\n\tgithub.com/uber-go/mapdecode v1.0.0 // indirect\n\tgithub.com/uber/jaeger-lib v2.4.1+incompatible // indirect\n\tgithub.com/uber/tchannel-go v1.32.1 // indirect\n\tgo.uber.org/atomic v1.11.0 // indirect\n\tgo.uber.org/dig v1.17.0 // indirect\n\tgo.uber.org/fx v1.13.1 // indirect\n\tgo.uber.org/multierr v1.6.0 // indirect\n\tgo.uber.org/net/metrics v1.3.0 // indirect\n\tgo.uber.org/thriftrw v1.29.2 // indirect\n\tgolang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect\n\tgolang.org/x/lint v0.0.0-20200130185559-910be7a94367 // indirect\n\tgolang.org/x/mod v0.8.0 // indirect\n\tgolang.org/x/net v0.19.0 // indirect\n\tgolang.org/x/oauth2 v0.1.0 // indirect\n\tgolang.org/x/sys v0.15.0 // indirect\n\tgolang.org/x/text v0.14.0 // indirect\n\tgolang.org/x/time v0.0.0-20170927054726-6dc17368e09b // indirect\n\tgolang.org/x/tools v0.6.0 // indirect\n\tgoogle.golang.org/appengine v1.6.7 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce // indirect\n\tgoogle.golang.org/grpc v1.28.0 // indirect\n\tgoogle.golang.org/protobuf v1.31.0 // indirect\n\thonnef.co/go/tools v0.3.2 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=\ngithub.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=\ngithub.com/HdrHistogram/hdrhistogram-go v0.9.0 h1:dpujRju0R4M/QZzcnR1LH1qm+TVG3UzkWdp5tH1WMcg=\ngithub.com/HdrHistogram/hdrhistogram-go v0.9.0/go.mod h1:nxrse8/Tzg2tg3DZcZjm6qEclQKK70g0KxO61gFFZD4=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=\ngithub.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=\ngithub.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=\ngithub.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=\ngithub.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY=\ngithub.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=\ngithub.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=\ngithub.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b h1:AP/Y7sqYicnjGDfD5VcY4CIfh1hRXBUavxrvELjTiOE=\ngithub.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q=\ngithub.com/cactus/go-statsd-client/statsd v0.0.0-20191106001114-12b4e2b38748/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw=\ngithub.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA=\ngithub.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA=\ngithub.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=\ngithub.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=\ngithub.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=\ngithub.com/gogo/googleapis v1.3.2 h1:kX1es4djPJrsDhY7aZKJy7aZasdcB5oSOEphMjSB53c=\ngithub.com/gogo/googleapis v1.3.2/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/gogo/status v1.1.0 h1:+eIkrewn5q6b30y+g/BJINVVdi2xH7je5MPJ3ZPK3JA=\ngithub.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=\ngithub.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=\ngithub.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=\ngithub.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3-0.20190920234318-1680a479a2cf/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=\ngithub.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=\ngithub.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=\ngithub.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=\ngithub.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=\ngithub.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=\ngithub.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=\ngithub.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=\ngithub.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/m3db/prometheus_client_golang v0.8.1 h1:t7w/tcFws81JL1j5sqmpqcOyQOpH4RDOmIe3A3fdN3w=\ngithub.com/m3db/prometheus_client_golang v0.8.1/go.mod h1:8R/f1xYhXWq59KD/mbRqoBulXejss7vYtYzWmruNUwI=\ngithub.com/m3db/prometheus_client_model v0.1.0 h1:cg1+DiuyT6x8h9voibtarkH1KT6CmsewBSaBhe8wzLo=\ngithub.com/m3db/prometheus_client_model v0.1.0/go.mod h1:Qfsxn+LypxzF+lNhak7cF7k0zxK7uB/ynGYoj80zcD4=\ngithub.com/m3db/prometheus_common v0.1.0 h1:YJu6eCIV6MQlcwND24cRG/aRkZDX1jvYbsNNs1ZYr0w=\ngithub.com/m3db/prometheus_common v0.1.0/go.mod h1:EBmDQaMAy4B8i+qsg1wMXAelLNVbp49i/JOeVszQ/rs=\ngithub.com/m3db/prometheus_procfs v0.8.1 h1:LsxWzVELhDU9sLsZTaFLCeAwCn7bC7qecZcK4zobs/g=\ngithub.com/m3db/prometheus_procfs v0.8.1/go.mod h1:N8lv8fLh3U3koZx1Bnisj60GYUMDpWb09x1R+dmMOJo=\ngithub.com/marusama/semaphore/v2 v2.5.0 h1:o/1QJD9DBYOWRnDhPwDVAXQn6mQYD0gZaS1Tpx6DJGM=\ngithub.com/marusama/semaphore/v2 v2.5.0/go.mod h1:z9nMiNUekt/LTpTUQdpp+4sJeYqUGpwMHfW0Z8V8fnQ=\ngithub.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=\ngithub.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=\ngithub.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=\ngithub.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=\ngithub.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a h1:AA9vgIBDjMHPC2McaGPojgV2dcI78ZC0TLNhYCXEKH8=\ngithub.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a/go.mod h1:lzZQ3Noex5pfAy7mkAeCjcBDteYU85uWWnJ/y6gKU8k=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=\ngithub.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=\ngithub.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=\ngithub.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=\ngithub.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s=\ngithub.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=\ngithub.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=\ngithub.com/prometheus/common v0.8.0/go.mod h1:PC/OgXc+UN7B4ALwvn1yzVZmVwvhXp5JsbBv6wSv6i0=\ngithub.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=\ngithub.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=\ngithub.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=\ngithub.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=\ngithub.com/prometheus/procfs v0.0.9/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=\ngithub.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=\ngithub.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=\ngithub.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/samuel/go-thrift v0.0.0-20191111193933-5165175b40af h1:EiWVfh8mr40yFZEui2oF0d45KgH48PkB2H0Z0GANvSI=\ngithub.com/samuel/go-thrift v0.0.0-20191111193933-5165175b40af/go.mod h1:Vrkh1pnjV9Bl8c3P9zH0/D4NlOHWP5d4/hF4YTULaec=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=\ngithub.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU=\ngithub.com/streadway/quantile v0.0.0-20220407130108-4246515d968d h1:X4+kt6zM/OVO6gbJdAfJR60MGPsqCzbtXNnjoGqdfAs=\ngithub.com/streadway/quantile v0.0.0-20220407130108-4246515d968d/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=\ngithub.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=\ngithub.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg=\ngithub.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=\ngithub.com/uber-common/bark v1.2.1/go.mod h1:g0ZuPcD7XiExKHynr93Q742G/sbrdVQkghrqLGOoFuY=\ngithub.com/uber-go/mapdecode v1.0.0 h1:euUEFM9KnuCa1OBixz1xM+FIXmpixyay5DLymceOVrU=\ngithub.com/uber-go/mapdecode v1.0.0/go.mod h1:b5nP15FwXTgpjTjeA9A2uTHXV5UJCl4arwKpP0FP1Hw=\ngithub.com/uber-go/tally v3.3.12+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU=\ngithub.com/uber-go/tally v3.3.15+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU=\ngithub.com/uber-go/tally v3.4.3+incompatible h1:Oq25FXV8cWHPRo+EPeNdbN3LfuozC9mDK2/4vZ1k38U=\ngithub.com/uber-go/tally v3.4.3+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU=\ngithub.com/uber/cadence-idl v0.0.0-20250616185004-cc6f52f87bc6 h1:YJlEu9Unzifwdn6SuE4rrl4zJ5lop5gBfSX8AyodTww=\ngithub.com/uber/cadence-idl v0.0.0-20250616185004-cc6f52f87bc6/go.mod h1:oyUK7GCNCRHCCyWyzifSzXpVrRYVBbAMHAzF5dXiKws=\ngithub.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=\ngithub.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=\ngithub.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=\ngithub.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=\ngithub.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=\ngithub.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=\ngithub.com/uber/ringpop-go v0.8.5/go.mod h1:zVI6eGO6L7pG14GkntHsSOfmUAWQ7B4lvmzly4IT4ls=\ngithub.com/uber/tchannel-go v1.16.0/go.mod h1:Rrgz1eL8kMjW/nEzZos0t+Heq0O4LhnUJVA32OvWKHo=\ngithub.com/uber/tchannel-go v1.22.2/go.mod h1:Rrgz1eL8kMjW/nEzZos0t+Heq0O4LhnUJVA32OvWKHo=\ngithub.com/uber/tchannel-go v1.32.1 h1:0Pu5kdZceabAt7Rr4pUC4YRpMJkE/tTfReMZdlvDjnU=\ngithub.com/uber/tchannel-go v1.32.1/go.mod h1:yT2EUp6YperZ0Tb/jwDX9gVEeiSG74r/L3CjF7zNJHs=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=\ngo.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=\ngo.uber.org/cadence v1.3.1-rc.11 h1:+AVmN+w1zV01BGjjK2S/PGsddo1Q5UkJt2B9ore/FWA=\ngo.uber.org/cadence v1.3.1-rc.11/go.mod h1:Yf2WaRFj6TtqrUbGRWxLU/8Vou3WPn0M2VIdfEIlEjE=\ngo.uber.org/dig v1.8.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw=\ngo.uber.org/dig v1.10.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw=\ngo.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI=\ngo.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU=\ngo.uber.org/fx v1.10.0/go.mod h1:vLRicqpG/qQEzno4SYU86iCwfT95EZza+Eba0ItuxqY=\ngo.uber.org/fx v1.13.1 h1:CFNTr1oin5OJ0VCZ8EycL3wzF29Jz2g0xe55RFsf2a4=\ngo.uber.org/fx v1.13.1/go.mod h1:bREWhavnedxpJeTq9pQT53BbvwhUv7TcpsOqcH4a+3w=\ngo.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI=\ngo.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=\ngo.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=\ngo.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=\ngo.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=\ngo.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/net/metrics v1.3.0 h1:iRLPuVecNYf/wIV+mQaA4IgN8ghifu3q1B4IT6HfwyY=\ngo.uber.org/net/metrics v1.3.0/go.mod h1:pEQrSDGNWT5IVpekWzee5//uHjI4gmgZFkobfw3bv8I=\ngo.uber.org/thriftrw v1.25.0/go.mod h1:IcIfSeZgc59AlYb0xr0DlDKIdD7SgjnFpG9BXCPyy9g=\ngo.uber.org/thriftrw v1.29.2 h1:pRuFLzbGvTcnYwGSjizWRHlbJUzGhu84sRiL1h1kUd8=\ngo.uber.org/thriftrw v1.29.2/go.mod h1:YcjXveberDd28/Bs34SwHy3yu85x/jB4UA2gIcz/Eo0=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/yarpc v1.55.0/go.mod h1:V2JUPDWHYGNpvyuroYjf0KFjwvBCtcFJLuvZqv7TWA0=\ngo.uber.org/yarpc v1.60.0 h1:rNRrS8C0sDk5EScc9epftMdKQeToJ4Nwl2hqTZ1knTY=\ngo.uber.org/yarpc v1.60.0/go.mod h1:fZ4SfvI/7sN9rF/H1W31g3yfbUmVv5fe+6xUSFPJC9E=\ngo.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=\ngo.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=\ngo.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM=\ngolang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367 h1:0IiAsCRByjO2QjX7ZPkw5oU9x+n1YqRL802rjC0c3Aw=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=\ngolang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=\ngolang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y=\ngolang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=\ngolang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200117145432-59e60aa80a0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=\ngolang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=\ngolang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/time v0.0.0-20170927054726-6dc17368e09b h1:3X+R0qq1+64izd8es+EttB6qcY+JDlVmAhpRXl7gpzU=\ngolang.org/x/time v0.0.0-20170927054726-6dc17368e09b/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191030062658-86caa796c7ab/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191114200427-caa0b0f7d508/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191226212025-6b505debf4bc/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117215004-fe56e6335763/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=\ngolang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=\ngoogle.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce h1:1mbrb1tUU+Zmt5C94IGKADBTJZjZXAd+BubWi7r9EiI=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=\ngoogle.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.3.2 h1:ytYb4rOqyp1TSa2EPvNVwtPQJctSELKaMyLfqNP4+34=\nhonnef.co/go/tools v0.3.2/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\n"
  },
  {
    "path": "k8s/README.md",
    "content": "# Cadence Samples Usage Guide\n\nThis guide explains how to build, deploy, and use the Cadence samples container for testing workflows.\n\n## Prerequisites: Domain Registration\n\nBefore running any samples, you must first register the domain in Cadence. Execute this command in the cadence-frontend pod:\n\n```bash\n# Access the cadence-frontend pod\nkubectl exec -it <cadence-frontend-pod-name> -n cadence -- /bin/bash\n\n# Register the default domain\ncadence --address $(hostname -i):7833 \\\n    --transport grpc \\\n    --domain default \\\n    domain register \\\n    --retention 1\n```\n\n**Note**: Replace `<cadence-frontend-pod-name>` with your actual cadence-frontend pod name and adjust the namespace if needed.\n\n## Building the Docker Image\n\nBuild the samples image with your Cadence host configuration:\n\n```bash\ndocker build --build-arg CADENCE_HOST=\"cadence-frontend.cadence.svc.cluster.local:7833\" -t cadence-samples:latest .\n```\n\n**Important**: Replace `cadence-frontend.cadence.svc.cluster.local:7833` with your actual Cadence frontend service address.\n\n### Examples for Different Environments\n\n```bash\n# Local development\ndocker build --build-arg CADENCE_HOST=\"localhost:7833\" -t cadence-samples:latest -f Dockerfile.samples .\n\n# Kubernetes cluster (same namespace)\ndocker build --build-arg CADENCE_HOST=\"cadence-frontend.cadence.svc.cluster.local:7833\" -t cadence-samples:latest .\n\n# Different namespace\ndocker build --build-arg CADENCE_HOST=\"cadence-frontend.my-namespace.svc.cluster.local:7833\" -t cadence-samples:latest .\n```\n\n## Upload to Container Registry\n\nTag and push your image to your container registry:\n\n```bash\n# Tag the image\ndocker tag cadence-samples:latest your-registry.com/cadence-samples:latest\n\n# Push to registry\ndocker push your-registry.com/cadence-samples:latest\n```\n\n## Kubernetes Deployment\n\n### Pod Configuration\n\nEdit the provided YAML file:\n\n```yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  name: cadence-samples\n  namespace: cadence # Change to your namespace\n  labels:\n    app: cadence-samples\nspec:\n  containers:\n  - name: cadence-samples\n    image: cadence-samples:latest  # Change to your registry image\n    imagePullPolicy: IfNotPresent\n    command: [\"/bin/bash\"]\n    args: [\"-c\", \"sleep infinity\"]\n    workingDir: /home/cadence\n    env:\n    - name: HOME\n      value: \"/home/cadence\"\n    resources:\n      requests:\n        memory: \"128Mi\"\n        cpu: \"100m\"\n      limits:\n        memory: \"1Gi\"\n        cpu: \"1\"\n  restartPolicy: Always\n  securityContext:\n    runAsUser: 1001\n    runAsGroup: 1001\n    fsGroup: 1001\n```\n\n**Required Changes**:\n1. **`namespace`**: Change to your Cadence namespace\n2. **`image`**: Change to your registry image path\n\n### Deploy the Pod\n\n```bash\nkubectl apply -f cadence-samples-pod.yaml\n```\n\n## Using the Samples\n\n### Step 1: Access the Container\n\n```bash\nkubectl exec -it cadence-samples -n cadence -- /bin/bash\n```\n\n### Step 2: Run Workflow Examples\n\n#### Terminal 1 - Start the Worker\n```bash\n# Example: Hello World worker\n./bin/helloworld -m worker\n```\n\n#### Terminal 2 - Trigger the Workflow\nOpen a second terminal and execute:\n```bash\nkubectl exec -it cadence-samples -n cadence -- /bin/bash\n./bin/helloworld -m trigger\n```\n\n#### Stop the Worker\nIn Terminal 1, press `Ctrl+C` to stop the worker.\n\n### Some Available Sample Commands\n\n```bash\n# Hello World\n./bin/helloworld -m worker\n./bin/helloworld -m trigger\n\n# File Processing\n./bin/fileprocessing -m worker\n./bin/fileprocessing -m trigger\n\n# DSL Example\n./bin/dsl -m worker\n./bin/dsl -m trigger -dslConfig cmd/samples/dsl/workflow1.yaml\n./bin/dsl -m trigger -dslConfig cmd/samples/dsl/workflow2.yaml\n```\n\n## Complete Sample Documentation\n\nFor all available samples, detailed explanations, and source code, visit:\n**https://github.com/cadence-workflow/cadence-samples**\n\nThis repository contains comprehensive documentation for each sample workflow pattern and advanced usage examples."
  },
  {
    "path": "k8s/cadence-samples-pod.yaml",
    "content": "apiVersion: v1\nkind: Pod\nmetadata:\n  name: cadence-samples\n  namespace: cadence # Replace with your cadence namespace\n  labels:\n    app: cadence-samples\nspec:\n  containers:\n  - name: cadence-samples\n    image: cadence-samples:latest  # Replace with your built image\n    imagePullPolicy: IfNotPresent\n    command: [\"/bin/bash\"]\n    args: [\"-c\", \"sleep infinity\"]\n    workingDir: /home/cadence\n    env:\n    - name: HOME\n      value: \"/home/cadence\"\n    resources:\n      requests:\n        memory: \"128Mi\"\n        cpu: \"100m\"\n      limits:\n        memory: \"1Gi\"\n        cpu: \"1\"\n  restartPolicy: Always\n  securityContext:\n    runAsUser: 1001\n    runAsGroup: 1001\n    fsGroup: 1001"
  },
  {
    "path": "k8s/docker/Dockerfile",
    "content": "FROM golang:1.21-alpine\n\n# Install all necessary dependencies for building and running\nRUN apk add --no-cache git make gcc musl-dev ca-certificates nano curl bash sed\n\n# Build argument for Cadence host configuration\nARG CADENCE_HOST=localhost:7833\n\n# Create non-root user\nRUN addgroup -g 1001 cadence && \\\n    adduser -D -u 1001 -G cadence cadence\n\n# Set working directory\nWORKDIR /home/cadence\n\n# Clone cadence-samples repository\nRUN git clone https://github.com/cadence-workflow/cadence-samples.git .\n\n# Update config file with the provided Cadence host\nRUN sed -i \"s/host: \\\"localhost:7833\\\"/host: \\\"${CADENCE_HOST}\\\"/\" config/development.yaml\n\n# Build all samples\nRUN make\n\n# Change ownership of files\nRUN chown -R cadence:cadence /home/cadence\n\n# Switch to non-root user\nUSER cadence\n\n# Default command - interactive shell\nCMD [\"/bin/bash\"]"
  },
  {
    "path": "new_samples/README.md",
    "content": "# Cadence Samples\n\nThis directory contains samples demonstrating various Cadence workflow concepts. Each sample is self-contained in its own concept folder.\n\n## Available Samples\n\n| Folder | Description |\n|--------|-------------|\n| [activities/](activities/) | Activity patterns: dynamic execution by name, parallel execution with pick-first |\n| [client_tls/](client_tls/) | Client-side TLS configuration for secure Cadence connections |\n| [data/](data/) | Custom DataConverters: gzip compression, AES-256-GCM encryption, and S3 \"claim-check\" offload |\n| [hello_world/](hello_world/) | Basic \"Hello World\" workflow and activity |\n| [operations/](operations/) | Workflow operations: cancellation and cleanup patterns |\n| [query/](query/) | Workflow query patterns |\n| [signal/](signal/) | Workflow signal patterns |\n\n## Prerequisites\n\n1. Install Cadence CLI: [https://cadenceworkflow.io/docs/cli/](https://cadenceworkflow.io/docs/cli/)\n2. Run the Cadence server:\n   ```bash\n   git clone https://github.com/cadence-workflow/cadence.git\n   cd cadence\n   docker compose -f docker/docker-compose.yml up\n   ```\n3. Open [localhost:8088](http://localhost:8088) to view Cadence UI\n4. Register the `cadence-samples` domain:\n   ```bash\n   cadence --domain cadence-samples domain register\n   ```\n\n## Running a Sample\n\nEach sample folder is self-contained. Navigate to any sample folder and run:\n\n```bash\ngo run .\n```\n\nThis starts the worker for that sample. Then use the Cadence CLI to start workflows as described in each sample's README.\n\n---\n\n## Adding a New Sample\n\nNew samples should follow the template-based structure for consistency. The `template/` directory contains Go templates that generate boilerplate code.\n\n### Step 1: Create Your Sample Folder\n\n```bash\nmkdir my_sample\ncd my_sample\n```\n\n### Step 2: Create Your Workflow Code\n\nCreate a file (e.g., `my_workflow.go`) with `package main`:\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"go.uber.org/cadence/activity\"\n    \"go.uber.org/cadence/workflow\"\n    \"time\"\n)\n\nfunc MyWorkflow(ctx workflow.Context) (string, error) {\n    ao := workflow.ActivityOptions{\n        ScheduleToStartTimeout: time.Minute,\n        StartToCloseTimeout:    time.Minute,\n    }\n    ctx = workflow.WithActivityOptions(ctx, ao)\n\n    var result string\n    err := workflow.ExecuteActivity(ctx, MyActivity).Get(ctx, &result)\n    return result, err\n}\n\nfunc MyActivity(ctx context.Context) (string, error) {\n    return \"Hello from my sample!\", nil\n}\n```\n\n### Step 3: Create the Generator\n\nCreate `generator/generate.go`:\n\n```go\npackage main\n\nimport \"github.com/uber-common/cadence-samples/new_samples/template\"\n\nfunc main() {\n    data := template.TemplateData{\n        SampleName: \"My Sample\",\n        Workflows:  []string{\"MyWorkflow\"},\n        Activities: []string{\"MyActivity\"},\n    }\n\n    template.GenerateAll(data)\n}\n```\n\n### Step 4: Create Sample-Specific Documentation\n\nCreate `generator/README_specific.md` with documentation specific to your sample:\n\n```markdown\n## My Sample\n\nDescription of what this sample demonstrates...\n\n### Start the workflow\n\n\\```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.MyWorkflow \\\n  --tl cadence-samples-worker \\\n  --et 60 \\\n  --input '{}'\n\\```\n```\n\n### Step 5: Run the Generator\n\n```bash\ncd generator\ngo run generate.go\n```\n\nThis generates:\n- `../worker.go` - Worker setup and registration\n- `../main.go` - Entry point that starts the worker\n- `../README.md` - Combined documentation\n- `README.md` - Generator-specific README\n\n### Template Files\n\nThe `template/` directory contains:\n\n| File | Purpose |\n|------|---------|\n| `generator.go` | Go code that powers the generation |\n| `worker.tmpl` | Template for worker.go |\n| `main.tmpl` | Template for main.go |\n| `README.tmpl` | Template for README header (prerequisites) |\n| `README_references.tmpl` | Template for README footer (references) |\n| `README_generator.tmpl` | Template for generator/README.md |\n\n## Learn More\n\n- [Cadence Documentation](https://cadenceworkflow.io/docs)\n- [Cadence Go Client](https://github.com/uber-go/cadence-client)\n- [Cadence Server](https://github.com/uber/cadence)\n"
  },
  {
    "path": "new_samples/activities/README.md",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# Activities Sample\n\n## Prerequisites\n\n0. Install Cadence CLI. See instruction [here](https://cadenceworkflow.io/docs/cli/).\n1. Run the Cadence server:\n    1. Clone the [Cadence](https://github.com/cadence-workflow/cadence) repository if you haven't done already: `git clone https://github.com/cadence-workflow/cadence.git`\n    2. Run `docker compose -f docker/docker-compose.yml up` to start Cadence server\n    3. See more details at https://github.com/uber/cadence/blob/master/README.md\n2. Once everything is up and running in Docker, open [localhost:8088](localhost:8088) to view Cadence UI.\n3. Register the `cadence-samples` domain:\n\n```bash\ncadence --domain cadence-samples domain register\n```\n\nRefresh the [domains page](http://localhost:8088/domains) from step 2 to verify `cadence-samples` is registered.\n\n## Steps to run sample\n\nInside the folder this sample is defined, run the following command:\n\n```bash\ngo run .\n```\n\nThis will call the main function in main.go which starts the worker, which will be execute the sample workflow code\n\n## Samples in this folder\n\nThis folder contains samples demonstrating various activity patterns in Cadence.\n\n### Dynamic Workflow\n\nThe `DynamicWorkflow` demonstrates executing an activity by its registered string name rather than passing the function reference directly. This pattern is useful when you need to dynamically determine which activity to execute at runtime.\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.DynamicWorkflow \\\n  --tl cadence-samples-worker \\\n  --et 60 \\\n  --input '{\"message\":\"Cadence\"}'\n```\n\n### Parallel Branch Pick First Workflow\n\nThe `ParallelBranchPickFirstWorkflow` demonstrates running multiple activities in parallel and returning the result of the first one to complete. This pattern is useful for scenarios like:\n- Racing multiple data sources\n- Implementing timeouts with fallbacks\n- Redundant execution for reliability\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.ParallelBranchPickFirstWorkflow \\\n  --tl cadence-samples-worker \\\n  --et 60 \\\n  --input '{}'\n```\n\nThe workflow will:\n1. Start two parallel activities with different delays\n2. Wait for the first one to complete\n3. Cancel the remaining activity\n4. Return the first successful result\n\nNote: The `WaitForCancellation` option is set to `true` to demonstrate proper cleanup of cancelled activities. In production, you may set this to `false` if you don't need to wait for cancellation acknowledgment.\n\n## References\n\n* The website: https://cadenceworkflow.io\n* Cadence's server: https://github.com/uber/cadence\n* Cadence's Go client: https://github.com/uber-go/cadence-client\n\n"
  },
  {
    "path": "new_samples/activities/dynamic_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n\t\"time\"\n)\n\nconst DynamicGreetingActivityName = \"cadence_samples.DynamicGreetingActivity\"\n\ntype dynamicWorkflowInput struct {\n\tMessage string `json:\"message\"`\n}\n\nfunc DynamicWorkflow(ctx workflow.Context, input dynamicWorkflowInput) (string, error) {\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"DynamicWorkflow started\")\n\n\tvar greetingMsg string\n\terr := workflow.ExecuteActivity(ctx, DynamicGreetingActivityName, input.Message).Get(ctx, &greetingMsg)\n\tif err != nil {\n\t\tlogger.Error(\"DynamicGreetingActivity failed\", zap.Error(err))\n\t\treturn \"\", err\n\t}\n\n\tlogger.Info(\"Workflow result\", zap.String(\"greeting\", greetingMsg))\n\treturn greetingMsg, nil\n}\n\nfunc DynamicGreetingActivity(ctx context.Context, message string) (string, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"DynamicGreetingActivity started.\")\n\treturn \"Hello, \" + message, nil\n}\n"
  },
  {
    "path": "new_samples/activities/generator/README.md",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# Sample Generator\n\nThis folder is NOT part of the actual sample. It exists only for contributors who work on this sample. Please disregard it if you are trying to learn about Cadence.\n\nTo create a better learning experience for Cadence users, each sample folder is designed to be self contained. Users can view every part of writing and running workflows, including:\n\n* Cadence client initialization\n* Worker with workflow and activity registrations\n* Workflow starter\n* and the workflow code itself\n\nSome samples may have more or fewer parts depending on what they need to demonstrate.\n\nIn most cases, the workflow code (e.g. `workflow.go`) is the part that users care about. The rest is boilerplate needed to run that workflow. For each sample folder, the workflow code should be written by hand. The boilerplate can be generated. Keeping all parts inside one folder gives early learners more value because they can see everything together rather than jumping across directories.\n\n## Contributing\n\n* When creating a new sample, follow the steps mentioned in the README file in the main samples folder.\n* To update the sample workflow code, edit the workflow file directly.\n* To update the worker, client, or other boilerplate logic, edit the generator file. If your change applies to all samples, update the common generator file inside the `template` folder. Edit the generator file in this folder only when the change should affect this sample alone.\n* When you are done run the following command in the generator folder\n\n```bash\ngo run .\n```\n"
  },
  {
    "path": "new_samples/activities/generator/README_specific.md",
    "content": "## Samples in this folder\n\nThis folder contains samples demonstrating various activity patterns in Cadence.\n\n### Dynamic Workflow\n\nThe `DynamicWorkflow` demonstrates executing an activity by its registered string name rather than passing the function reference directly. This pattern is useful when you need to dynamically determine which activity to execute at runtime.\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.DynamicWorkflow \\\n  --tl cadence-samples-worker \\\n  --et 60 \\\n  --input '{\"message\":\"Cadence\"}'\n```\n\n### Parallel Branch Pick First Workflow\n\nThe `ParallelBranchPickFirstWorkflow` demonstrates running multiple activities in parallel and returning the result of the first one to complete. This pattern is useful for scenarios like:\n- Racing multiple data sources\n- Implementing timeouts with fallbacks\n- Redundant execution for reliability\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.ParallelBranchPickFirstWorkflow \\\n  --tl cadence-samples-worker \\\n  --et 60 \\\n  --input '{}'\n```\n\nThe workflow will:\n1. Start two parallel activities with different delays\n2. Wait for the first one to complete\n3. Cancel the remaining activity\n4. Return the first successful result\n\nNote: The `WaitForCancellation` option is set to `true` to demonstrate proper cleanup of cancelled activities. In production, you may set this to `false` if you don't need to wait for cancellation acknowledgment.\n"
  },
  {
    "path": "new_samples/activities/generator/generate.go",
    "content": "package main\n\nimport \"github.com/uber-common/cadence-samples/new_samples/template\"\n\nfunc main() {\n\t// Define the data for Activities samples\n\tdata := template.TemplateData{\n\t\tSampleName: \"Activities\",\n\t\tWorkflows:  []string{\"DynamicWorkflow\", \"ParallelBranchPickFirstWorkflow\"},\n\t\tActivities: []string{\"DynamicGreetingActivity\", \"ParallelActivity\"},\n\t}\n\n\ttemplate.GenerateAll(data)\n}\n\n// Implement custom generator below\n"
  },
  {
    "path": "new_samples/activities/main.go",
    "content": "// THIS IS A GENERATED FILE\n// PLEASE DO NOT EDIT\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc main() {\n\tStartWorker()\n\n\tdone := make(chan os.Signal, 1)\n\tsignal.Notify(done, syscall.SIGINT)\n\tfmt.Println(\"Cadence worker started, press ctrl+c to terminate...\")\n\t<-done\n}\n"
  },
  {
    "path": "new_samples/activities/parallel_pick_first_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"time\"\n)\n\ntype parallelBranchInput struct {\n\tMessage string `json:\"message\"`\n}\n\n// ParallelBranchPickFirstWorkflow is a sample workflow simulating two parallel activities running\n// at the same time and picking the first successful result.\nfunc ParallelBranchPickFirstWorkflow(ctx workflow.Context) (string, error) {\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"ParallelBranchPickFirstWorkflow started\")\n\n\tselector := workflow.NewSelector(ctx)\n\tvar firstResp string\n\n\t// Use a cancel handler to cancel all rest of other activities.\n\tchildCtx, cancelHandler := workflow.WithCancel(ctx)\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t\tHeartbeatTimeout:       time.Second * 20,\n\t\tWaitForCancellation:    true, // wait for cancellation to complete\n\t}\n\tchildCtx = workflow.WithActivityOptions(childCtx, ao)\n\n\t// Set WaitForCancellation to true to demonstrate the cancellation to the other activities. In real world case,\n\t// you might not care about them and could set WaitForCancellation to false (which is default value).\n\n\t// Run two activities in parallel\n\tf1 := workflow.ExecuteActivity(childCtx, ParallelActivity, parallelBranchInput{Message: \"first activity\"}, time.Second*10)\n\tf2 := workflow.ExecuteActivity(childCtx, ParallelActivity, parallelBranchInput{Message: \"second activity\"}, time.Second*2)\n\tpendingFutures := []workflow.Future{f1, f2}\n\tselector.AddFuture(f1, func(f workflow.Future) {\n\t\tf.Get(ctx, &firstResp)\n\t}).AddFuture(f2, func(f workflow.Future) {\n\t\tf.Get(ctx, &firstResp)\n\t})\n\n\t// wait for any of the future to complete\n\tselector.Select(ctx)\n\n\t// now if at least one future is complete, cancel all other pending futures\n\tcancelHandler()\n\n\t// - If you want to wait for pending activities to finish after issuing cancellation\n\t// then wait for the future to complete.\n\t// - if you don't want to wait for completion of pending activities cancellation then you can choose to\n\t// set WaitForCancellation to false through WithWaitForCancellation(false)\n\tfor _, f := range pendingFutures {\n\t\terr := f.Get(ctx, &firstResp)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\tlogger.Info(\"ParallelBranchPickFirstWorkflow completed\")\n\treturn firstResp, nil\n}\n\nfunc ParallelActivity(ctx context.Context, input parallelBranchInput) (string, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"ParallelActivity started\")\n\treturn \"Hello \" + input.Message, nil\n}\n"
  },
  {
    "path": "new_samples/activities/worker.go",
    "content": "// THIS IS A GENERATED FILE\n// PLEASE DO NOT EDIT\n\n// Package worker implements a Cadence worker with basic configurations.\npackage main\n\nimport (\n\t\"github.com/uber-go/tally\"\n\tapiv1 \"github.com/uber/cadence-idl/go/proto/api/v1\"\n\t\"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/compatibility\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/yarpc\"\n\t\"go.uber.org/yarpc/peer\"\n\tyarpchostport \"go.uber.org/yarpc/peer/hostport\"\n\t\"go.uber.org/yarpc/transport/grpc\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nconst (\n\tHostPort = \"127.0.0.1:7833\"\n\tDomain   = \"cadence-samples\"\n\t// TaskListName identifies set of client workflows, activities, and workers.\n\t// It could be your group or client or application name.\n\tTaskListName   = \"cadence-samples-worker\"\n\tClientName     = \"cadence-samples-worker\"\n\tCadenceService = \"cadence-frontend\"\n)\n\n// StartWorker creates and starts a basic Cadence worker.\nfunc StartWorker() {\n\tlogger, cadenceClient := BuildLogger(), BuildCadenceClient()\n\tworkerOptions := worker.Options{\n\t\tLogger:       logger,\n\t\tMetricsScope: tally.NewTestScope(TaskListName, nil),\n\t}\n\n\tw := worker.New(\n\t\tcadenceClient,\n\t\tDomain,\n\t\tTaskListName,\n\t\tworkerOptions)\n\t// workflow registration\n\tw.RegisterWorkflowWithOptions(DynamicWorkflow, workflow.RegisterOptions{Name: \"cadence_samples.DynamicWorkflow\"})\n\tw.RegisterWorkflowWithOptions(ParallelBranchPickFirstWorkflow, workflow.RegisterOptions{Name: \"cadence_samples.ParallelBranchPickFirstWorkflow\"})\n\tw.RegisterActivityWithOptions(DynamicGreetingActivity, activity.RegisterOptions{Name: \"cadence_samples.DynamicGreetingActivity\"})\n\tw.RegisterActivityWithOptions(ParallelActivity, activity.RegisterOptions{Name: \"cadence_samples.ParallelActivity\"})\n\n\terr := w.Start()\n\tif err != nil {\n\t\tpanic(\"Failed to start worker: \" + err.Error())\n\t}\n\tlogger.Info(\"Started Worker.\", zap.String(\"worker\", TaskListName))\n\n}\n\nfunc BuildCadenceClient(dialOptions ...grpc.DialOption) workflowserviceclient.Interface {\n\tgrpcTransport := grpc.NewTransport()\n\t// Create a single peer chooser that identifies the host/port and configures\n\t// a gRPC dialer with TLS credentials\n\tmyChooser := peer.NewSingle(\n\t\tyarpchostport.Identify(HostPort),\n\t\tgrpcTransport.NewDialer(dialOptions...),\n\t)\n\toutbound := grpcTransport.NewOutbound(myChooser)\n\n\tdispatcher := yarpc.NewDispatcher(yarpc.Config{\n\t\tName: ClientName,\n\t\tOutbounds: yarpc.Outbounds{\n\t\t\tCadenceService: {Unary: outbound},\n\t\t},\n\t})\n\tif err := dispatcher.Start(); err != nil {\n\t\tpanic(\"Failed to start dispatcher: \" + err.Error())\n\t}\n\n\tclientConfig := dispatcher.ClientConfig(CadenceService)\n\n\t// Create a compatibility adapter that wraps proto-based YARPC clients\n\t// to provide a unified interface for domain, workflow, worker, and visibility APIs\n\treturn compatibility.NewThrift2ProtoAdapter(\n\t\tapiv1.NewDomainAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkflowAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkerAPIYARPCClient(clientConfig),\n\t\tapiv1.NewVisibilityAPIYARPCClient(clientConfig),\n\t)\n}\n\nfunc BuildLogger() *zap.Logger {\n\tconfig := zap.NewDevelopmentConfig()\n\tconfig.Level.SetLevel(zapcore.InfoLevel)\n\n\tvar err error\n\tlogger, err := config.Build()\n\tif err != nil {\n\t\tpanic(\"Failed to setup logger: \" + err.Error())\n\t}\n\n\treturn logger\n}\n"
  },
  {
    "path": "new_samples/client_tls/README.md",
    "content": "# Client TLS Sample\n\nThis sample demonstrates how to connect to a Cadence server using TLS (Transport Layer Security) for secure communication.\n\n## Prerequisites\n\n1. A Cadence server configured with TLS enabled\n2. Client certificates in the `credentials/` directory:\n   - `credentials/client.crt` - Client certificate\n   - `credentials/client.key` - Client private key\n   - `credentials/keytest.crt` - Server CA certificate\n\n## Running the Sample\n\n### Register a Domain\n\nBefore starting workflows, you may need to register a domain:\n\n```bash\ngo run . register-domain\n```\n\n### Start a Workflow\n\nTo start a HelloWorld workflow with TLS:\n\n```bash\ngo run . start-workflow\n```\n\n**Note:** This requires a worker running to execute the workflow. Start a worker from the `hello_world/` sample first.\n\n## What This Sample Demonstrates\n\n### TLS Configuration\n\nThe `tls_config.go` file shows how to:\n- Load client certificates for mutual TLS authentication\n- Load server CA certificates for server verification\n- Configure TLS options for gRPC connections\n\n### Cadence Client Setup\n\nThe `cadence_client.go` file shows how to:\n- Create a YARPC dispatcher with TLS-enabled gRPC transport\n- Build a Cadence client using the proto API adapter\n\n### Client Operations\n\nThe `main.go` file demonstrates:\n- Starting a workflow execution programmatically\n- Registering a domain programmatically\n\n## Security Notes\n\n- The sample uses `InsecureSkipVerify: true` for development convenience\n- In production, always verify server certificates by setting `InsecureSkipVerify: false`\n- Store credentials securely and never commit them to version control\n\n## References\n\n- [Cadence TLS Documentation](https://cadenceworkflow.io/docs/operation-guide/tls)\n- [Go TLS Configuration](https://pkg.go.dev/crypto/tls)\n"
  },
  {
    "path": "new_samples/client_tls/cadence_client.go",
    "content": "package main\n\nimport (\n\tapiv1 \"github.com/uber/cadence-idl/go/proto/api/v1\"\n\t\"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient\"\n\t\"go.uber.org/cadence/compatibility\"\n\t\"go.uber.org/yarpc\"\n\t\"go.uber.org/yarpc/peer\"\n\tyarpchostport \"go.uber.org/yarpc/peer/hostport\"\n\t\"go.uber.org/yarpc/transport/grpc\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nconst (\n\tHostPort       = \"127.0.0.1:7833\"\n\tClientName     = \"cadence-samples-client\"\n\tCadenceService = \"cadence-frontend\"\n)\n\n// BuildCadenceClient creates a Cadence client with optional TLS dial options.\nfunc BuildCadenceClient(dialOptions ...grpc.DialOption) workflowserviceclient.Interface {\n\tgrpcTransport := grpc.NewTransport()\n\tmyChooser := peer.NewSingle(\n\t\tyarpchostport.Identify(HostPort),\n\t\tgrpcTransport.NewDialer(dialOptions...),\n\t)\n\toutbound := grpcTransport.NewOutbound(myChooser)\n\n\tdispatcher := yarpc.NewDispatcher(yarpc.Config{\n\t\tName: ClientName,\n\t\tOutbounds: yarpc.Outbounds{\n\t\t\tCadenceService: {Unary: outbound},\n\t\t},\n\t})\n\tif err := dispatcher.Start(); err != nil {\n\t\tpanic(\"Failed to start dispatcher: \" + err.Error())\n\t}\n\n\tclientConfig := dispatcher.ClientConfig(CadenceService)\n\n\treturn compatibility.NewThrift2ProtoAdapter(\n\t\tapiv1.NewDomainAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkflowAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkerAPIYARPCClient(clientConfig),\n\t\tapiv1.NewVisibilityAPIYARPCClient(clientConfig),\n\t)\n}\n\n// BuildLogger creates a zap logger for the client.\nfunc BuildLogger() *zap.Logger {\n\tconfig := zap.NewDevelopmentConfig()\n\tconfig.Level.SetLevel(zapcore.InfoLevel)\n\n\tlogger, err := config.Build()\n\tif err != nil {\n\t\tpanic(\"Failed to setup logger: \" + err.Error())\n\t}\n\n\treturn logger\n}\n"
  },
  {
    "path": "new_samples/client_tls/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"go.uber.org/cadence/.gen/go/shared\"\n\t\"go.uber.org/zap\"\n)\n\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tprintUsage()\n\t\tos.Exit(1)\n\t}\n\n\tcommand := os.Args[1]\n\tswitch command {\n\tcase \"start-workflow\":\n\t\tstartWorkflow()\n\tcase \"register-domain\":\n\t\tregisterDomain()\n\tdefault:\n\t\tfmt.Printf(\"Unknown command: %s\\n\", command)\n\t\tprintUsage()\n\t\tos.Exit(1)\n\t}\n}\n\nfunc printUsage() {\n\tfmt.Println(\"Usage: go run . <command>\")\n\tfmt.Println()\n\tfmt.Println(\"Commands:\")\n\tfmt.Println(\"  start-workflow   Start a HelloWorld workflow with TLS\")\n\tfmt.Println(\"  register-domain  Register a domain with TLS\")\n}\n\nfunc startWorkflow() {\n\tlogger := BuildLogger()\n\tlogger.Info(\"Starting workflow with TLS...\")\n\n\ttlsDialOption, err := BuildTLSDialOption()\n\tif err != nil {\n\t\tlogger.Fatal(\"Failed to build TLS dial option\", zap.Error(err))\n\t}\n\n\tcadenceClient := BuildCadenceClient(tlsDialOption)\n\n\tdomain := \"default\"\n\ttasklist := \"cadence-samples-worker\"\n\tworkflowID := uuid.New().String()\n\trequestID := uuid.New().String()\n\texecutionTimeout := int32(60)\n\tcloseTimeout := int32(60)\n\n\tworkflowType := \"cadence_samples.HelloWorldWorkflow\"\n\tinput := []byte(`{\"message\": \"Cadence\"}`)\n\n\treq := shared.StartWorkflowExecutionRequest{\n\t\tDomain:     &domain,\n\t\tWorkflowId: &workflowID,\n\t\tWorkflowType: &shared.WorkflowType{\n\t\t\tName: &workflowType,\n\t\t},\n\t\tTaskList: &shared.TaskList{\n\t\t\tName: &tasklist,\n\t\t},\n\t\tInput:                               input,\n\t\tExecutionStartToCloseTimeoutSeconds: &executionTimeout,\n\t\tTaskStartToCloseTimeoutSeconds:      &closeTimeout,\n\t\tRequestId:                           &requestID,\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), time.Minute)\n\tdefer cancel()\n\n\tresp, err := cadenceClient.StartWorkflowExecution(ctx, &req)\n\tif err != nil {\n\t\tlogger.Fatal(\"Failed to start workflow\", zap.Error(err))\n\t}\n\n\tlogger.Info(\"Successfully started HelloWorld workflow\",\n\t\tzap.String(\"workflowID\", workflowID),\n\t\tzap.String(\"runID\", resp.GetRunId()))\n}\n\nfunc registerDomain() {\n\tlogger := BuildLogger()\n\tlogger.Info(\"Registering domain with TLS...\")\n\n\ttlsDialOption, err := BuildTLSDialOption()\n\tif err != nil {\n\t\tlogger.Fatal(\"Failed to build TLS dial option\", zap.Error(err))\n\t}\n\n\tcadenceClient := BuildCadenceClient(tlsDialOption)\n\n\tdomain := \"default\"\n\tretentionDays := int32(7)\n\temitMetric := true\n\tdescription := \"Default domain for cadence samples\"\n\n\treq := &shared.RegisterDomainRequest{\n\t\tName:                                   &domain,\n\t\tDescription:                            &description,\n\t\tWorkflowExecutionRetentionPeriodInDays: &retentionDays,\n\t\tEmitMetric:                             &emitMetric,\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\terr = cadenceClient.RegisterDomain(ctx, req)\n\tif err != nil {\n\t\tif _, ok := err.(*shared.DomainAlreadyExistsError); ok {\n\t\t\tlogger.Info(\"Domain already exists\", zap.String(\"domain\", domain))\n\t\t\treturn\n\t\t}\n\t\tlogger.Fatal(\"Failed to register domain\", zap.Error(err))\n\t}\n\n\tlogger.Info(\"Successfully registered domain\", zap.String(\"domain\", domain))\n}\n"
  },
  {
    "path": "new_samples/client_tls/tls_config.go",
    "content": "package main\n\nimport (\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"go.uber.org/yarpc/transport/grpc\"\n\t\"google.golang.org/grpc/credentials\"\n)\n\n// BuildTLSDialOption creates a gRPC dial option with TLS credentials for secure\n// connection to a Cadence server with mutual TLS enabled.\nfunc BuildTLSDialOption() (grpc.DialOption, error) {\n\t// Load client certificate for mutual TLS\n\tclientCert, err := tls.LoadX509KeyPair(\"credentials/client.crt\", \"credentials/client.key\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to load client certificate: %w\", err)\n\t}\n\n\t// Load server CA certificate\n\tcaCert, err := os.ReadFile(\"credentials/keytest.crt\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to load server CA certificate: %w\", err)\n\t}\n\n\tcaCertPool := x509.NewCertPool()\n\tif !caCertPool.AppendCertsFromPEM(caCert) {\n\t\treturn nil, fmt.Errorf(\"failed to append CA certificate\")\n\t}\n\n\ttlsConfig := &tls.Config{\n\t\tInsecureSkipVerify: true, // For development only - verify certs in production\n\t\tRootCAs:            caCertPool,\n\t\tCertificates:       []tls.Certificate{clientCert},\n\t\tMinVersion:         tls.VersionTLS12,\n\t}\n\n\tcreds := credentials.NewTLS(tlsConfig)\n\treturn grpc.DialerCredentials(creds), nil\n}\n"
  },
  {
    "path": "new_samples/concurrency/README.md",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# Concurrency Sample\n\n## Prerequisites\n\n0. Install Cadence CLI. See instruction [here](https://cadenceworkflow.io/docs/cli/).\n1. Run the Cadence server:\n    1. Clone the [Cadence](https://github.com/cadence-workflow/cadence) repository if you haven't done already: `git clone https://github.com/cadence-workflow/cadence.git`\n    2. Run `docker compose -f docker/docker-compose.yml up` to start Cadence server\n    3. See more details at https://github.com/uber/cadence/blob/master/README.md\n2. Once everything is up and running in Docker, open [localhost:8088](localhost:8088) to view Cadence UI.\n3. Register the `cadence-samples` domain:\n\n```bash\ncadence --domain cadence-samples domain register\n```\n\nRefresh the [domains page](http://localhost:8088/domains) from step 2 to verify `cadence-samples` is registered.\n\n## Steps to run sample\n\nInside the folder this sample is defined, run the following command:\n\n```bash\ngo run .\n```\n\nThis will call the main function in main.go which starts the worker, which will be execute the sample workflow code\n\n## Samples in this folder\n\nThis folder contains samples demonstrating concurrency control patterns in Cadence.\n\n### Batch Processing Workflow\n\nThe `BatchWorkflow` demonstrates how to process large batches of activities with controlled concurrency using Cadence's `workflow.NewBatchFuture` functionality.\n\n#### The Problem It Solves\n\nWhen processing large datasets (thousands of records, files, or API calls), you face a dilemma:\n- **Sequential processing**: Too slow, poor user experience\n- **Unlimited concurrency**: Overwhelms databases, APIs, or downstream services\n- **Manual concurrency control**: Complex error handling and resource management\n- **Cadence limits**: Max 1024 pending activities per workflow\n\n#### The Solution\n\n`workflow.NewBatchFuture` provides a robust solution:\n\n- **Controlled Concurrency**: Process items in parallel while respecting system limits\n- **Automatic Error Handling**: Failed activities don't crash the entire batch\n- **Resource Efficiency**: Optimal throughput without overwhelming downstream services\n- **Built-in Observability**: Monitoring, retries, and failure tracking\n- **Workflow Integration**: Seamless integration with Cadence's workflow engine\n\n#### Real-World Scenarios\n\n- Processing 10,000 user records for a migration\n- Sending emails to 50,000 subscribers\n- Generating reports for 1,000 customers\n- Processing files in a data pipeline\n\n#### Sample Behavior\n\n- Creates a configurable number of activities (default: 10)\n- Executes them with controlled concurrency (default: 3)\n- Simulates work with random delays (900-999ms per activity)\n- Handles cancellation gracefully\n\n#### Technical Considerations\n\n- **Cadence limit**: Maximum 1024 pending activities per workflow\n- **Resource management**: Controlled concurrency prevents system overload\n- **Error handling**: Failed activities don't crash the entire batch\n\n#### How to Start the Workflow\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.BatchWorkflow \\\n  --tl cadence-samples-worker \\\n  --et 300 \\\n  --input '{\"Concurrency\":3,\"TotalSize\":10}'\n```\n\nYou can adjust the parameters:\n- `Concurrency`: Maximum number of activities running in parallel\n- `TotalSize`: Total number of activities to process\n\n## References\n\n* The website: https://cadenceworkflow.io\n* Cadence's server: https://github.com/uber/cadence\n* Cadence's Go client: https://github.com/uber-go/cadence-client\n\n"
  },
  {
    "path": "new_samples/concurrency/batch_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/workflow\"\n)\n\n// BatchWorkflowInput configures the batch processing parameters.\ntype BatchWorkflowInput struct {\n\tConcurrency int // Maximum number of activities running in parallel\n\tTotalSize   int // Total number of activities to process\n}\n\n// BatchWorkflow demonstrates processing large batches of activities with controlled\n// concurrency using workflow.NewBatchFuture. This pattern is useful for:\n// - Processing thousands of records without overwhelming downstream services\n// - Respecting the 1024 pending activities limit per workflow\n// - Automatic error handling and retry management\nfunc BatchWorkflow(ctx workflow.Context, input BatchWorkflowInput) error {\n\t// Create activity factories for each task (not yet executed)\n\tfactories := make([]func(workflow.Context) workflow.Future, input.TotalSize)\n\tfor taskID := 0; taskID < input.TotalSize; taskID++ {\n\t\ttaskID := taskID // Capture loop variable for closure\n\t\tfactories[taskID] = func(ctx workflow.Context) workflow.Future {\n\t\t\t// Configure activity timeouts\n\t\t\taCtx := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{\n\t\t\t\tScheduleToStartTimeout: time.Minute * 1,\n\t\t\t\tStartToCloseTimeout:    time.Minute * 1,\n\t\t\t})\n\t\t\treturn workflow.ExecuteActivity(aCtx, BatchActivity, taskID)\n\t\t}\n\t}\n\n\t// Execute all activities with controlled concurrency\n\tbatch, err := workflow.NewBatchFuture(ctx, input.Concurrency, factories)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create batch future: %w\", err)\n\t}\n\n\t// Wait for all activities to complete\n\treturn batch.Get(ctx, nil)\n}\n\n// BatchActivity simulates a unit of work that takes 900-999ms to complete.\n// In real applications, this would be your actual processing logic.\nfunc BatchActivity(ctx context.Context, taskID int) error {\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn fmt.Errorf(\"batch activity %d failed: %w\", taskID, ctx.Err())\n\tcase <-time.After(time.Duration(rand.Int63n(100))*time.Millisecond + 900*time.Millisecond):\n\t\treturn nil\n\t}\n}\n"
  },
  {
    "path": "new_samples/concurrency/batch_workflow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/cadence/testsuite\"\n)\n\nfunc Test_BatchWorkflow(t *testing.T) {\n\t// Create test environment for workflow testing\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\tenv := testSuite.NewTestWorkflowEnvironment()\n\n\t// Register the workflow and activity functions\n\tenv.RegisterWorkflow(BatchWorkflow)\n\tenv.RegisterActivity(BatchActivity)\n\n\t// Execute workflow with 3 concurrent workers processing 10 tasks\n\tenv.ExecuteWorkflow(BatchWorkflow, BatchWorkflowInput{\n\t\tConcurrency: 3,\n\t\tTotalSize:   10,\n\t})\n\n\t// Assert workflow completed successfully without errors\n\tassert.True(t, env.IsWorkflowCompleted())\n\tassert.Nil(t, env.GetWorkflowError())\n}\n"
  },
  {
    "path": "new_samples/concurrency/generator/README.md",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# Sample Generator\n\nThis folder is NOT part of the actual sample. It exists only for contributors who work on this sample. Please disregard it if you are trying to learn about Cadence.\n\nTo create a better learning experience for Cadence users, each sample folder is designed to be self contained. Users can view every part of writing and running workflows, including:\n\n* Cadence client initialization\n* Worker with workflow and activity registrations\n* Workflow starter\n* and the workflow code itself\n\nSome samples may have more or fewer parts depending on what they need to demonstrate.\n\nIn most cases, the workflow code (e.g. `workflow.go`) is the part that users care about. The rest is boilerplate needed to run that workflow. For each sample folder, the workflow code should be written by hand. The boilerplate can be generated. Keeping all parts inside one folder gives early learners more value because they can see everything together rather than jumping across directories.\n\n## Contributing\n\n* When creating a new sample, follow the steps mentioned in the README file in the main samples folder.\n* To update the sample workflow code, edit the workflow file directly.\n* To update the worker, client, or other boilerplate logic, edit the generator file. If your change applies to all samples, update the common generator file inside the `template` folder. Edit the generator file in this folder only when the change should affect this sample alone.\n* When you are done run the following command in the generator folder\n\n```bash\ngo run .\n```\n"
  },
  {
    "path": "new_samples/concurrency/generator/README_specific.md",
    "content": "## Samples in this folder\n\nThis folder contains samples demonstrating concurrency control patterns in Cadence.\n\n### Batch Processing Workflow\n\nThe `BatchWorkflow` demonstrates how to process large batches of activities with controlled concurrency using Cadence's `workflow.NewBatchFuture` functionality.\n\n#### The Problem It Solves\n\nWhen processing large datasets (thousands of records, files, or API calls), you face a dilemma:\n- **Sequential processing**: Too slow, poor user experience\n- **Unlimited concurrency**: Overwhelms databases, APIs, or downstream services\n- **Manual concurrency control**: Complex error handling and resource management\n- **Cadence limits**: Max 1024 pending activities per workflow\n\n#### The Solution\n\n`workflow.NewBatchFuture` provides a robust solution:\n\n- **Controlled Concurrency**: Process items in parallel while respecting system limits\n- **Automatic Error Handling**: Failed activities don't crash the entire batch\n- **Resource Efficiency**: Optimal throughput without overwhelming downstream services\n- **Built-in Observability**: Monitoring, retries, and failure tracking\n- **Workflow Integration**: Seamless integration with Cadence's workflow engine\n\n#### Real-World Scenarios\n\n- Processing 10,000 user records for a migration\n- Sending emails to 50,000 subscribers\n- Generating reports for 1,000 customers\n- Processing files in a data pipeline\n\n#### Sample Behavior\n\n- Creates a configurable number of activities (default: 10)\n- Executes them with controlled concurrency (default: 3)\n- Simulates work with random delays (900-999ms per activity)\n- Handles cancellation gracefully\n\n#### Technical Considerations\n\n- **Cadence limit**: Maximum 1024 pending activities per workflow\n- **Resource management**: Controlled concurrency prevents system overload\n- **Error handling**: Failed activities don't crash the entire batch\n\n#### How to Start the Workflow\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.BatchWorkflow \\\n  --tl cadence-samples-worker \\\n  --et 300 \\\n  --input '{\"Concurrency\":3,\"TotalSize\":10}'\n```\n\nYou can adjust the parameters:\n- `Concurrency`: Maximum number of activities running in parallel\n- `TotalSize`: Total number of activities to process\n"
  },
  {
    "path": "new_samples/concurrency/generator/generate.go",
    "content": "package main\n\nimport \"github.com/uber-common/cadence-samples/new_samples/template\"\n\nfunc main() {\n\t// Define the data for Concurrency samples\n\tdata := template.TemplateData{\n\t\tSampleName: \"Concurrency\",\n\t\tWorkflows:  []string{\"BatchWorkflow\"},\n\t\tActivities: []string{\"BatchActivity\"},\n\t}\n\n\ttemplate.GenerateAll(data)\n}\n\n// Implement custom generator below\n"
  },
  {
    "path": "new_samples/concurrency/main.go",
    "content": "// THIS IS A GENERATED FILE\n// PLEASE DO NOT EDIT\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc main() {\n\tStartWorker()\n\n\tdone := make(chan os.Signal, 1)\n\tsignal.Notify(done, syscall.SIGINT)\n\tfmt.Println(\"Cadence worker started, press ctrl+c to terminate...\")\n\t<-done\n}\n"
  },
  {
    "path": "new_samples/concurrency/worker.go",
    "content": "// THIS IS A GENERATED FILE\n// PLEASE DO NOT EDIT\n\n// Package worker implements a Cadence worker with basic configurations.\npackage main\n\nimport (\n\t\"github.com/uber-go/tally\"\n\tapiv1 \"github.com/uber/cadence-idl/go/proto/api/v1\"\n\t\"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/compatibility\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/yarpc\"\n\t\"go.uber.org/yarpc/peer\"\n\tyarpchostport \"go.uber.org/yarpc/peer/hostport\"\n\t\"go.uber.org/yarpc/transport/grpc\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nconst (\n\tHostPort = \"127.0.0.1:7833\"\n\tDomain   = \"cadence-samples\"\n\t// TaskListName identifies set of client workflows, activities, and workers.\n\t// It could be your group or client or application name.\n\tTaskListName   = \"cadence-samples-worker\"\n\tClientName     = \"cadence-samples-worker\"\n\tCadenceService = \"cadence-frontend\"\n)\n\n// StartWorker creates and starts a basic Cadence worker.\nfunc StartWorker() {\n\tlogger, cadenceClient := BuildLogger(), BuildCadenceClient()\n\tworkerOptions := worker.Options{\n\t\tLogger:       logger,\n\t\tMetricsScope: tally.NewTestScope(TaskListName, nil),\n\t}\n\n\tw := worker.New(\n\t\tcadenceClient,\n\t\tDomain,\n\t\tTaskListName,\n\t\tworkerOptions)\n\t// workflow registration\n\tw.RegisterWorkflowWithOptions(BatchWorkflow, workflow.RegisterOptions{Name: \"cadence_samples.BatchWorkflow\"})\n\tw.RegisterActivityWithOptions(BatchActivity, activity.RegisterOptions{Name: \"cadence_samples.BatchActivity\"})\n\n\terr := w.Start()\n\tif err != nil {\n\t\tpanic(\"Failed to start worker: \" + err.Error())\n\t}\n\tlogger.Info(\"Started Worker.\", zap.String(\"worker\", TaskListName))\n\n}\n\nfunc BuildCadenceClient(dialOptions ...grpc.DialOption) workflowserviceclient.Interface {\n\tgrpcTransport := grpc.NewTransport()\n\t// Create a single peer chooser that identifies the host/port and configures\n\t// a gRPC dialer with TLS credentials\n\tmyChooser := peer.NewSingle(\n\t\tyarpchostport.Identify(HostPort),\n\t\tgrpcTransport.NewDialer(dialOptions...),\n\t)\n\toutbound := grpcTransport.NewOutbound(myChooser)\n\n\tdispatcher := yarpc.NewDispatcher(yarpc.Config{\n\t\tName: ClientName,\n\t\tOutbounds: yarpc.Outbounds{\n\t\t\tCadenceService: {Unary: outbound},\n\t\t},\n\t})\n\tif err := dispatcher.Start(); err != nil {\n\t\tpanic(\"Failed to start dispatcher: \" + err.Error())\n\t}\n\n\tclientConfig := dispatcher.ClientConfig(CadenceService)\n\n\t// Create a compatibility adapter that wraps proto-based YARPC clients\n\t// to provide a unified interface for domain, workflow, worker, and visibility APIs\n\treturn compatibility.NewThrift2ProtoAdapter(\n\t\tapiv1.NewDomainAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkflowAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkerAPIYARPCClient(clientConfig),\n\t\tapiv1.NewVisibilityAPIYARPCClient(clientConfig),\n\t)\n}\n\nfunc BuildLogger() *zap.Logger {\n\tconfig := zap.NewDevelopmentConfig()\n\tconfig.Level.SetLevel(zapcore.InfoLevel)\n\n\tvar err error\n\tlogger, err := config.Build()\n\tif err != nil {\n\t\tpanic(\"Failed to setup logger: \" + err.Error())\n\t}\n\n\treturn logger\n}\n"
  },
  {
    "path": "new_samples/data/README.md",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# Data Sample\n\n## Prerequisites\n\n0. Install Cadence CLI. See instruction [here](https://cadenceworkflow.io/docs/cli/).\n1. Run the Cadence server:\n    1. Clone the [Cadence](https://github.com/cadence-workflow/cadence) repository if you haven't done already: `git clone https://github.com/cadence-workflow/cadence.git`\n    2. Run `docker compose -f docker/docker-compose.yml up` to start Cadence server\n    3. See more details at https://github.com/uber/cadence/blob/master/README.md\n2. Once everything is up and running in Docker, open [localhost:8088](localhost:8088) to view Cadence UI.\n3. Register the `cadence-samples` domain:\n\n```bash\ncadence --domain cadence-samples domain register\n```\n\nRefresh the [domains page](http://localhost:8088/domains) from step 2 to verify `cadence-samples` is registered.\n\n## Steps to run sample\n\nInside the folder this sample is defined, run the following command:\n\n```bash\ngo run .\n```\n\nThis will call the main function in main.go which starts the worker, which will be execute the sample workflow code\n\n## Data Converter Samples\n\nThis folder demonstrates three production-ready patterns for custom `DataConverter` implementations in Cadence. A `DataConverter` controls how every workflow input, output, and activity parameter is serialized before it is written to Cadence history — making it the right place to add compression, encryption, or external offloading without changing any workflow or activity code.\n\n### What is a DataConverter?\n\nA `DataConverter` implements two methods:\n\n- `ToData(value ...interface{}) ([]byte, error)` — called before data is written to Cadence history\n- `FromData(input []byte, valuePtr ...interface{}) error` — called when data is read back\n\nThe same `DataConverter` must be used by **both the worker and any client that triggers or queries the workflow**. In this sample the workflows generate their own payloads internally, so they can be started from the Cadence CLI without bundling a custom converter into the CLI itself.\n\nEach sample runs its own worker on its own task list so it can use its own `DataConverter`. Start all three with a single `go run .`.\n\n---\n\n### Compression Sample\n\n`CompressionDataConverterWorkflow` demonstrates gzip-over-JSON compression. For repetitive JSON data this typically achieves 60–80% size reduction, lowering storage costs and bandwidth for large workflow payloads.\n\n**Task list:** `cadence-samples-data-compression`\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.CompressionDataConverterWorkflow \\\n  --tl cadence-samples-data-compression \\\n  --et 60\n```\n\nWhen the worker starts it prints a compression statistics banner showing the before/after sizes of the sample payload so you can see the benefit immediately.\n\n---\n\n### Encryption Sample\n\n`EncryptionDataConverterWorkflow` demonstrates AES-256-GCM encryption. Every workflow input, output, and activity parameter is encrypted before being written to Cadence history. Without the key, the data stored by the Cadence server — including any operators browsing workflow history — is completely opaque.\n\nThe sample uses a `SensitiveCustomerRecord` containing realistic PII and PHI fields (name, email, SSN, credit card, medical notes) to make the use case concrete.\n\n**Task list:** `cadence-samples-data-encryption`\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.EncryptionDataConverterWorkflow \\\n  --tl cadence-samples-data-encryption \\\n  --et 60\n```\n\n#### Encryption key\n\nBy default, the worker uses a hardcoded demo key and prints a prominent warning. To use your own key:\n\n```bash\n# Generate a random 32-byte (256-bit) key\nexport CADENCE_ENCRYPTION_KEY=$(openssl rand -hex 32)\ngo run .\n```\n\n> **WARNING:** The hardcoded demo key (`cadence-demo-key-NOT-FOR-PROD!!!`) is public.\n> Never use it in production. In production, load your key from a secrets manager\n> (AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager, etc.).\n\n#### How AES-256-GCM works\n\n- `ToData`: JSON-encode arguments → generate a 12-byte random nonce → `cipher.AEAD.Seal` → return `nonce || ciphertext+tag`.\n- `FromData`: split nonce from input → `cipher.AEAD.Open` → JSON-decode.\n\nThe GCM authentication tag (16 bytes) ensures ciphertext tampering is detected. The random nonce means the same plaintext produces different ciphertext on every call, preventing replay detection by an attacker who observes Cadence history.\n\n---\n\n### S3 Offload Sample (Claim-Check Pattern)\n\n`S3OffloadDataConverterWorkflow` demonstrates the *claim-check* pattern: payloads larger than a configurable threshold are stored in an external `BlobStore` and only a small reference (a few dozen bytes) travels through Cadence workflow history.\n\nThis solves the practical problem of Cadence's per-payload size limits (~2 MB) for workflows that must pass very large datasets between the workflow and its activities.\n\n**Task list:** `cadence-samples-data-s3`\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.S3OffloadDataConverterWorkflow \\\n  --tl cadence-samples-data-s3 \\\n  --et 60\n```\n\n#### How it works\n\n- `ToData`: JSON-encode → if `len(json) > thresholdBytes`, upload to `BlobStore` under a SHA-256 key and return `0x01 || {\"__s3_ref\":\"<bucket>/<sha256hex>\"}`. Otherwise return `0x00 || json` inline.\n- `FromData`: read prefix byte → if `0x01`, fetch from `BlobStore` and decode; if `0x00`, decode inline.\n\n#### Default store (zero-config)\n\nOut of the box, `localFSBlobStore` writes blobs to `os.TempDir()/cadence-samples-data-s3/`. No cloud credentials or additional dependencies are needed.\n\n#### Swapping in real AWS S3\n\nThe file `s3_dataconverter_workflow.go` contains a commented `s3BlobStore` stub showing the exact AWS SDK calls needed. To enable it:\n\n1. Add the AWS SDK to your module:\n   ```bash\n   go get github.com/aws/aws-sdk-go-v2/config\n   go get github.com/aws/aws-sdk-go-v2/service/s3\n   ```\n2. Uncomment the `s3BlobStore` section in `s3_dataconverter_workflow.go`.\n3. Replace `NewLocalFSBlobStore()` with `NewS3BlobStore(bucket, region)` in `worker.go`.\n4. Set standard AWS environment variables (`AWS_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`) or use an IAM instance role.\n\nYou can also point the SDK at [LocalStack](https://localstack.cloud/) or [MinIO](https://min.io/) for local testing without a real AWS account.\n\n> **Note on cleanup:** The `s3OffloadDataConverter` does not delete blobs after the workflow completes. In production, use S3 object lifecycle policies to automatically expire old blobs.\n\n---\n\n### When to use which pattern\n\n| Pattern | Best for |\n|---------|----------|\n| **Compression** | Large repetitive JSON payloads; reducing storage cost without confidentiality requirements |\n| **Encryption** | PII, PHI, secrets, or any data that must be unreadable in Cadence history |\n| **S3 Offload** | Payloads approaching Cadence's size limits; binary or non-JSON data; cost-conscious archival |\n\nPatterns can be composed: encrypt-then-compress, or encrypt-then-offload to S3 for maximum security and minimum history size.\n\n## References\n\n* The website: https://cadenceworkflow.io\n* Cadence's server: https://github.com/uber/cadence\n* Cadence's Go client: https://github.com/uber-go/cadence-client\n\n"
  },
  {
    "path": "new_samples/data/compressed_dataconverter_workflow.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"reflect\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n// compressedJSONDataConverter implements encoded.DataConverter with gzip compression.\n// It serializes data to JSON, then compresses using gzip to reduce storage size.\ntype compressedJSONDataConverter struct{}\n\n// NewCompressedJSONDataConverter creates a new compressed JSON data converter.\nfunc NewCompressedJSONDataConverter() encoded.DataConverter {\n\treturn &compressedJSONDataConverter{}\n}\n\nfunc (dc *compressedJSONDataConverter) ToData(value ...interface{}) ([]byte, error) {\n\tvar jsonBuf bytes.Buffer\n\tenc := json.NewEncoder(&jsonBuf)\n\tfor i, obj := range value {\n\t\terr := enc.Encode(obj)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to encode argument: %d, %v, with error: %v\", i, reflect.TypeOf(obj), err)\n\t\t}\n\t}\n\n\tvar compressedBuf bytes.Buffer\n\tgzipWriter := gzip.NewWriter(&compressedBuf)\n\n\t_, err := gzipWriter.Write(jsonBuf.Bytes())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to compress data: %v\", err)\n\t}\n\n\terr = gzipWriter.Close()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to close gzip writer: %v\", err)\n\t}\n\n\treturn compressedBuf.Bytes(), nil\n}\n\nfunc (dc *compressedJSONDataConverter) FromData(input []byte, valuePtr ...interface{}) error {\n\t// Handle empty input (e.g., when workflow is started without --input from CLI)\n\tif len(input) == 0 {\n\t\treturn nil\n\t}\n\n\tgzipReader, err := gzip.NewReader(bytes.NewBuffer(input))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to create gzip reader: %v\", err)\n\t}\n\tdefer gzipReader.Close()\n\n\tdecompressedData, err := io.ReadAll(gzipReader)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to decompress data: %v\", err)\n\t}\n\n\tdec := json.NewDecoder(bytes.NewBuffer(decompressedData))\n\tfor i, obj := range valuePtr {\n\t\terr := dec.Decode(obj)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unable to decode argument: %d, %v, with error: %v\", i, reflect.TypeOf(obj), err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// LargePayload represents a complex data structure with nested objects and arrays\n// to demonstrate compression benefits.\ntype LargePayload struct {\n\tID          string                 `json:\"id\"`\n\tName        string                 `json:\"name\"`\n\tDescription string                 `json:\"description\"`\n\tMetadata    map[string]interface{} `json:\"metadata\"`\n\tItems       []Item                 `json:\"items\"`\n\tConfig      Config                 `json:\"config\"`\n\tHistory     []HistoryEntry         `json:\"history\"`\n\tTags        []string               `json:\"tags\"`\n\tStats       Statistics             `json:\"statistics\"`\n}\n\ntype Item struct {\n\tItemID      string            `json:\"item_id\"`\n\tTitle       string            `json:\"title\"`\n\tDescription string            `json:\"description\"`\n\tPrice       float64           `json:\"price\"`\n\tCategories  []string          `json:\"categories\"`\n\tAttributes  map[string]string `json:\"attributes\"`\n\tReviews     []Review          `json:\"reviews\"`\n\tInventory   Inventory         `json:\"inventory\"`\n}\n\ntype Review struct {\n\tReviewID   string  `json:\"review_id\"`\n\tUserID     string  `json:\"user_id\"`\n\tRating     int     `json:\"rating\"`\n\tComment    string  `json:\"comment\"`\n\tHelpful    int     `json:\"helpful_votes\"`\n\tNotHelpful int     `json:\"not_helpful_votes\"`\n\tDate       string  `json:\"date\"`\n\tVerified   bool    `json:\"verified_purchase\"`\n\tScore      float64 `json:\"score\"`\n}\n\ntype Inventory struct {\n\tQuantity    int    `json:\"quantity\"`\n\tLocation    string `json:\"location\"`\n\tLastUpdated string `json:\"last_updated\"`\n\tStatus      string `json:\"status\"`\n}\n\ntype Config struct {\n\tVersion     string            `json:\"version\"`\n\tEnvironment string            `json:\"environment\"`\n\tSettings    map[string]string `json:\"settings\"`\n\tFeatures    []string          `json:\"features\"`\n\tLimits      Limits            `json:\"limits\"`\n}\n\ntype Limits struct {\n\tMaxItems    int `json:\"max_items\"`\n\tMaxRequests int `json:\"max_requests_per_minute\"`\n\tMaxFileSize int `json:\"max_file_size_mb\"`\n\tMaxUsers    int `json:\"max_concurrent_users\"`\n\tTimeoutSecs int `json:\"timeout_seconds\"`\n}\n\ntype HistoryEntry struct {\n\tEventID   string            `json:\"event_id\"`\n\tTimestamp string            `json:\"timestamp\"`\n\tEventType string            `json:\"event_type\"`\n\tUserID    string            `json:\"user_id\"`\n\tDetails   map[string]string `json:\"details\"`\n\tSeverity  string            `json:\"severity\"`\n}\n\ntype Statistics struct {\n\tTotalItems     int     `json:\"total_items\"`\n\tTotalUsers     int     `json:\"total_users\"`\n\tAverageRating  float64 `json:\"average_rating\"`\n\tTotalRevenue   float64 `json:\"total_revenue\"`\n\tActiveOrders   int     `json:\"active_orders\"`\n\tCompletionRate float64 `json:\"completion_rate\"`\n}\n\n// CreateLargePayload creates a sample large payload with realistic data\n// to demonstrate compression benefits.\nfunc CreateLargePayload() LargePayload {\n\tlargeDescription := strings.Repeat(\"This is a comprehensive product catalog containing thousands of items with detailed descriptions, specifications, and user reviews. Each item includes pricing information, inventory status, and customer feedback. The catalog is designed to provide complete information for customers making purchasing decisions. \", 50)\n\n\titems := make([]Item, 100)\n\tfor i := 0; i < 100; i++ {\n\t\treviews := make([]Review, 25)\n\t\tfor j := 0; j < 25; j++ {\n\t\t\treviews[j] = Review{\n\t\t\t\tReviewID:   fmt.Sprintf(\"review_%d_%d\", i, j),\n\t\t\t\tUserID:     fmt.Sprintf(\"user_%d\", j),\n\t\t\t\tRating:     1 + (j % 5),\n\t\t\t\tComment:    strings.Repeat(\"This is a detailed customer review with comprehensive feedback about the product quality, delivery experience, and overall satisfaction. The customer provides specific details about their experience. \", 3),\n\t\t\t\tHelpful:    j * 2,\n\t\t\t\tNotHelpful: j,\n\t\t\t\tDate:       \"2024-01-15T10:30:00Z\",\n\t\t\t\tVerified:   j%2 == 0,\n\t\t\t\tScore:      float64(1+(j%5)) + float64(j%10)/10.0,\n\t\t\t}\n\t\t}\n\n\t\tattributes := make(map[string]string)\n\t\tfor k := 0; k < 20; k++ {\n\t\t\tattributes[fmt.Sprintf(\"attr_%d\", k)] = strings.Repeat(\"This is a detailed attribute description with comprehensive information about the product specification. \", 2)\n\t\t}\n\n\t\titems[i] = Item{\n\t\t\tItemID:      fmt.Sprintf(\"item_%d\", i),\n\t\t\tTitle:       fmt.Sprintf(\"High-Quality Product %d with Advanced Features\", i),\n\t\t\tDescription: strings.Repeat(\"This is a premium product with exceptional quality and advanced features designed for professional use. It includes comprehensive documentation and support. \", 10),\n\t\t\tPrice:       float64(100+i*10) + float64(i%100)/100.0,\n\t\t\tCategories:  []string{\"Electronics\", \"Professional\", \"Premium\", \"Advanced\"},\n\t\t\tAttributes:  attributes,\n\t\t\tReviews:     reviews,\n\t\t\tInventory: Inventory{\n\t\t\t\tQuantity:    100 + i,\n\t\t\t\tLocation:    fmt.Sprintf(\"Warehouse %d\", i%5),\n\t\t\t\tLastUpdated: \"2024-01-15T10:30:00Z\",\n\t\t\t\tStatus:      \"In Stock\",\n\t\t\t},\n\t\t}\n\t}\n\n\thistory := make([]HistoryEntry, 50)\n\tfor i := 0; i < 50; i++ {\n\t\tdetails := make(map[string]string)\n\t\tfor j := 0; j < 10; j++ {\n\t\t\tdetails[fmt.Sprintf(\"detail_%d\", j)] = strings.Repeat(\"This is a detailed event description with comprehensive information about the system event and its impact. \", 2)\n\t\t}\n\n\t\thistory[i] = HistoryEntry{\n\t\t\tEventID:   fmt.Sprintf(\"event_%d\", i),\n\t\t\tTimestamp: \"2024-01-15T10:30:00Z\",\n\t\t\tEventType: \"system_update\",\n\t\t\tUserID:    fmt.Sprintf(\"admin_%d\", i%5),\n\t\t\tDetails:   details,\n\t\t\tSeverity:  \"medium\",\n\t\t}\n\t}\n\n\tmetadata := make(map[string]interface{})\n\tfor i := 0; i < 30; i++ {\n\t\tmetadata[fmt.Sprintf(\"meta_key_%d\", i)] = strings.Repeat(\"This is comprehensive metadata information with detailed descriptions and specifications. \", 5)\n\t}\n\n\treturn LargePayload{\n\t\tID:          \"large_payload_001\",\n\t\tName:        \"Comprehensive Product Catalog\",\n\t\tDescription: largeDescription,\n\t\tMetadata:    metadata,\n\t\tItems:       items,\n\t\tConfig: Config{\n\t\t\tVersion:     \"2.1.0\",\n\t\t\tEnvironment: \"production\",\n\t\t\tSettings: map[string]string{\n\t\t\t\t\"cache_enabled\":     \"true\",\n\t\t\t\t\"compression_level\": \"high\",\n\t\t\t\t\"timeout\":           \"30s\",\n\t\t\t\t\"max_connections\":   \"1000\",\n\t\t\t\t\"retry_attempts\":    \"3\",\n\t\t\t},\n\t\t\tFeatures: []string{\"advanced_search\", \"real_time_updates\", \"analytics\", \"reporting\", \"integration\"},\n\t\t\tLimits: Limits{\n\t\t\t\tMaxItems:    10000,\n\t\t\t\tMaxRequests: 1000,\n\t\t\t\tMaxFileSize: 100,\n\t\t\t\tMaxUsers:    5000,\n\t\t\t\tTimeoutSecs: 30,\n\t\t\t},\n\t\t},\n\t\tHistory: history,\n\t\tTags:    []string{\"catalog\", \"products\", \"inventory\", \"analytics\", \"reporting\", \"integration\", \"api\", \"dashboard\"},\n\t\tStats: Statistics{\n\t\t\tTotalItems:     10000,\n\t\t\tTotalUsers:     5000,\n\t\t\tAverageRating:  4.2,\n\t\t\tTotalRevenue:   1250000.50,\n\t\t\tActiveOrders:   250,\n\t\t\tCompletionRate: 98.5,\n\t\t},\n\t}\n}\n\n// GetPayloadSizeInfo returns information about the payload size before and after compression.\nfunc GetPayloadSizeInfo(payload LargePayload, converter encoded.DataConverter) (int, int, float64, error) {\n\tjsonData, err := json.Marshal(payload)\n\tif err != nil {\n\t\treturn 0, 0, 0, fmt.Errorf(\"failed to marshal payload: %v\", err)\n\t}\n\toriginalSize := len(jsonData)\n\n\tcompressedData, err := converter.ToData(payload)\n\tif err != nil {\n\t\treturn 0, 0, 0, fmt.Errorf(\"failed to compress payload: %v\", err)\n\t}\n\tcompressedSize := len(compressedData)\n\n\tcompressionRatio := float64(compressedSize) / float64(originalSize)\n\tcompressionPercentage := (1.0 - compressionRatio) * 100\n\n\treturn originalSize, compressedSize, compressionPercentage, nil\n}\n\n// CompressionDataConverterWorkflow demonstrates processing large payloads with compression.\n// The DataConverter automatically compresses/decompresses all workflow data.\n// Note: The workflow generates its own payload internally so it can be started from CLI\n// without requiring the CLI to use the custom DataConverter. The compression demonstration\n// happens when data is passed between workflow and activity.\nfunc CompressionDataConverterWorkflow(ctx workflow.Context) (LargePayload, error) {\n\tlogger := workflow.GetLogger(ctx)\n\n\t// Generate the large payload internally - this allows the workflow to be started\n\t// from CLI without needing a custom DataConverter on the client side.\n\t// The compression benefit is demonstrated when passing data to/from activities.\n\tinput := CreateLargePayload()\n\n\tlogger.Info(\"Large payload workflow started\", zap.String(\"payload_id\", input.ID))\n\tlogger.Info(\"Processing large payload with compression\", zap.Int(\"items_count\", len(input.Items)))\n\n\tactivityOptions := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, activityOptions)\n\n\tvar result LargePayload\n\terr := workflow.ExecuteActivity(ctx, CompressionDataConverterActivity, input).Get(ctx, &result)\n\tif err != nil {\n\t\tlogger.Error(\"Large payload activity failed\", zap.Error(err))\n\t\treturn LargePayload{}, err\n\t}\n\n\tlogger.Info(\"Large payload workflow completed\", zap.String(\"result_id\", result.ID))\n\tlogger.Info(\"Note: All large payload data was automatically compressed/decompressed using gzip compression\")\n\treturn result, nil\n}\n\n// CompressionDataConverterActivity processes the large payload.\n// In production, this might involve data transformation, validation, etc.\nfunc CompressionDataConverterActivity(ctx context.Context, input LargePayload) (LargePayload, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"Large payload activity received input\", zap.String(\"payload_id\", input.ID), zap.Int(\"items_count\", len(input.Items)))\n\n\tinput.Name = input.Name + \" (Processed)\"\n\tinput.Stats.TotalItems = len(input.Items)\n\n\tlogger.Info(\"Large payload activity completed\", zap.String(\"result_id\", input.ID))\n\treturn input, nil\n}\n"
  },
  {
    "path": "new_samples/data/compressed_dataconverter_workflow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/testsuite\"\n\t\"go.uber.org/cadence/worker\"\n)\n\nfunc Test_CompressionDataConverterWorkflow(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\tenv := testSuite.NewTestWorkflowEnvironment()\n\tenv.RegisterWorkflow(CompressionDataConverterWorkflow)\n\tenv.RegisterActivity(CompressionDataConverterActivity)\n\n\tdataConverter := NewCompressedJSONDataConverter()\n\tworkerOptions := worker.Options{\n\t\tDataConverter: dataConverter,\n\t}\n\tenv.SetWorkerOptions(workerOptions)\n\n\tvar activityResult LargePayload\n\tenv.SetOnActivityCompletedListener(func(activityInfo *activity.Info, result encoded.Value, err error) {\n\t\tresult.Get(&activityResult)\n\t})\n\n\t// Workflow generates its own payload internally, no input needed\n\tenv.ExecuteWorkflow(CompressionDataConverterWorkflow)\n\n\trequire.True(t, env.IsWorkflowCompleted())\n\trequire.NoError(t, env.GetWorkflowError())\n\trequire.Equal(t, \"Comprehensive Product Catalog (Processed)\", activityResult.Name)\n\trequire.Equal(t, 100, activityResult.Stats.TotalItems)\n}\n"
  },
  {
    "path": "new_samples/data/encrypted_dataconverter_workflow.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"reflect\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n// encryptedJSONDataConverter implements encoded.DataConverter with AES-256-GCM encryption.\n// It serializes data to JSON, then encrypts using AES-256-GCM so that workflow history\n// stored in Cadence is opaque to anyone without the key.\ntype encryptedJSONDataConverter struct {\n\tgcm cipher.AEAD\n}\n\nvar errFailedToCreateConverter = errors.New(\"failed to create encrypted data converter\")\n\n// NewEncryptedJSONDataConverter creates a new encrypted JSON data converter.\n// key must be exactly 32 bytes (AES-256). Returns an error if the key is invalid.\nfunc NewEncryptedJSONDataConverter(key []byte) (encoded.DataConverter, error) {\n\tblock, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, errors.Join(errFailedToCreateConverter, err)\n\t}\n\tgcm, err := cipher.NewGCM(block)\n\tif err != nil {\n\t\treturn nil, errors.Join(errFailedToCreateConverter, err)\n\t}\n\treturn &encryptedJSONDataConverter{gcm: gcm}, nil\n}\n\n// demoEncryptionKey is a hardcoded 32-byte key used ONLY when CADENCE_ENCRYPTION_KEY is unset.\n// DO NOT use this key in production. Rotate your key and load it from a secrets manager.\nvar demoEncryptionKey = []byte(\"cadence-demo-key-NOT-FOR-PROD!!!\")\n\n// LoadEncryptionKey reads a 32-byte AES key from the CADENCE_ENCRYPTION_KEY environment\n// variable (hex-encoded, 64 hex chars). If the env var is unset, falls back to a hardcoded\n// demo key with a warning. If the env var is set but invalid, panics — silently falling back\n// to the public demo key when the user clearly intended their own key would be a security\n// hole.\nfunc LoadEncryptionKey() []byte {\n\thexKey := os.Getenv(\"CADENCE_ENCRYPTION_KEY\")\n\tif hexKey == \"\" {\n\t\tfmt.Println(\"WARNING: CADENCE_ENCRYPTION_KEY not set. Using hardcoded demo key.\")\n\t\tfmt.Println(\"WARNING: DO NOT USE THE DEMO KEY IN PRODUCTION.\")\n\t\treturn demoEncryptionKey\n\t}\n\tkey, err := hex.DecodeString(hexKey)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"CADENCE_ENCRYPTION_KEY is not valid hex: %v\", err))\n\t}\n\tif len(key) != 32 {\n\t\tpanic(fmt.Sprintf(\"CADENCE_ENCRYPTION_KEY must be exactly 64 hex chars (32 bytes), got %d hex chars (%d bytes)\", len(hexKey), len(key)))\n\t}\n\treturn key\n}\n\nfunc (dc *encryptedJSONDataConverter) ToData(value ...interface{}) ([]byte, error) {\n\tvar jsonBuf bytes.Buffer\n\tenc := json.NewEncoder(&jsonBuf)\n\tfor i, obj := range value {\n\t\tif err := enc.Encode(obj); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to encode argument: %d, %v, with error: %v\", i, reflect.TypeOf(obj), err)\n\t\t}\n\t}\n\n\tnonce := make([]byte, dc.gcm.NonceSize())\n\tif _, err := io.ReadFull(rand.Reader, nonce); err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to generate nonce: %v\", err)\n\t}\n\n\t// Seal appends the GCM authentication tag to the ciphertext.\n\t// Output layout: nonce (12 bytes) || ciphertext+tag\n\tciphertext := dc.gcm.Seal(nonce, nonce, jsonBuf.Bytes(), nil)\n\treturn ciphertext, nil\n}\n\nfunc (dc *encryptedJSONDataConverter) FromData(input []byte, valuePtr ...interface{}) error {\n\tif len(input) == 0 {\n\t\treturn nil\n\t}\n\n\tnonceSize := dc.gcm.NonceSize()\n\tif len(input) < nonceSize {\n\t\treturn fmt.Errorf(\"ciphertext too short: %d bytes\", len(input))\n\t}\n\n\tnonce, ciphertext := input[:nonceSize], input[nonceSize:]\n\tplaintext, err := dc.gcm.Open(nil, nonce, ciphertext, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"decryption failed: %v\", err)\n\t}\n\n\tdec := json.NewDecoder(bytes.NewBuffer(plaintext))\n\tfor i, obj := range valuePtr {\n\t\tif err := dec.Decode(obj); err != nil {\n\t\t\treturn fmt.Errorf(\"unable to decode argument: %d, %v, with error: %v\", i, reflect.TypeOf(obj), err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// SensitiveCustomerRecord represents PII/PHI data that must be encrypted in workflow history.\ntype SensitiveCustomerRecord struct {\n\tCustomerID    string `json:\"customer_id\"`\n\tFullName      string `json:\"full_name\"`\n\tEmail         string `json:\"email\"`\n\tSSN           string `json:\"ssn\"`\n\tCreditCard    string `json:\"credit_card_number\"`\n\tBillingAddr   string `json:\"billing_address\"`\n\tMedicalNotes  string `json:\"medical_notes\"`\n\tDiagnosisCode string `json:\"diagnosis_code\"`\n\tPrescriptions string `json:\"prescriptions\"`\n\tInsuranceID   string `json:\"insurance_id\"`\n\tProcessedBy   string `json:\"processed_by\"`\n}\n\n// CreateSensitiveCustomerRecord creates a sample customer record with realistic PII/PHI.\nfunc CreateSensitiveCustomerRecord() SensitiveCustomerRecord {\n\treturn SensitiveCustomerRecord{\n\t\tCustomerID:    \"cust_8a7f3b2e\",\n\t\tFullName:      \"Jane A. Doe\",\n\t\tEmail:         \"jane.doe@example.com\",\n\t\tSSN:           \"123-45-6789\",\n\t\tCreditCard:    \"4111-1111-1111-1111\",\n\t\tBillingAddr:   \"1234 Elm Street, Springfield, IL 62701\",\n\t\tMedicalNotes:  \"Patient presents with hypertension and type-2 diabetes. Advised dietary changes and increased physical activity. Follow-up scheduled in 3 months.\",\n\t\tDiagnosisCode: \"I10, E11.9\",\n\t\tPrescriptions: \"Lisinopril 10mg once daily; Metformin 500mg twice daily\",\n\t\tInsuranceID:   \"INS-987654321\",\n\t\tProcessedBy:   \"workflow-processor-v2\",\n\t}\n}\n\n// GetEncryptionSizeInfo returns the plaintext size, ciphertext size, and a hex preview of the ciphertext.\nfunc GetEncryptionSizeInfo(record SensitiveCustomerRecord, converter encoded.DataConverter) (int, int, string, error) {\n\tjsonData, err := json.Marshal(record)\n\tif err != nil {\n\t\treturn 0, 0, \"\", fmt.Errorf(\"failed to marshal record: %v\", err)\n\t}\n\tplaintextSize := len(jsonData)\n\n\tencrypted, err := converter.ToData(record)\n\tif err != nil {\n\t\treturn 0, 0, \"\", fmt.Errorf(\"failed to encrypt record: %v\", err)\n\t}\n\tciphertextSize := len(encrypted)\n\n\tpreview := hex.EncodeToString(encrypted)\n\tif len(preview) > 80 {\n\t\tpreview = preview[:80] + \"...\"\n\t}\n\n\treturn plaintextSize, ciphertextSize, preview, nil\n}\n\n// EncryptionDataConverterWorkflow demonstrates encrypting sensitive workflow data.\n// The DataConverter automatically encrypts all workflow inputs, outputs, and activity\n// parameters before they are stored in Cadence history. Without the key, the data\n// is unreadable even to Cadence operators viewing the workflow history.\n//\n// Note: The workflow generates its own payload internally so it can be started from\n// the Cadence CLI without requiring the CLI to use the custom DataConverter.\nfunc EncryptionDataConverterWorkflow(ctx workflow.Context) (SensitiveCustomerRecord, error) {\n\tlogger := workflow.GetLogger(ctx)\n\n\trecord := CreateSensitiveCustomerRecord()\n\tlogger.Info(\"Encryption workflow started\", zap.String(\"customer_id\", record.CustomerID))\n\tlogger.Info(\"All customer PII/PHI will be encrypted before storage in Cadence history\")\n\n\tactivityOptions := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, activityOptions)\n\n\tvar result SensitiveCustomerRecord\n\terr := workflow.ExecuteActivity(ctx, EncryptionDataConverterActivity, record).Get(ctx, &result)\n\tif err != nil {\n\t\tlogger.Error(\"Encryption workflow activity failed\", zap.Error(err))\n\t\treturn SensitiveCustomerRecord{}, err\n\t}\n\n\tlogger.Info(\"Encryption workflow completed\", zap.String(\"customer_id\", result.CustomerID))\n\tlogger.Info(\"Note: All PII/PHI was automatically encrypted/decrypted using AES-256-GCM\")\n\treturn result, nil\n}\n\n// EncryptionDataConverterActivity processes the sensitive customer record.\n// In production this might perform claims processing, fraud checks, etc.\nfunc EncryptionDataConverterActivity(ctx context.Context, record SensitiveCustomerRecord) (SensitiveCustomerRecord, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"Encryption activity received record\", zap.String(\"customer_id\", record.CustomerID))\n\n\trecord.ProcessedBy = record.ProcessedBy + \" (Encrypted)\"\n\n\tlogger.Info(\"Encryption activity completed\", zap.String(\"customer_id\", record.CustomerID))\n\treturn record, nil\n}\n"
  },
  {
    "path": "new_samples/data/encrypted_dataconverter_workflow_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/testsuite\"\n\t\"go.uber.org/cadence/worker\"\n)\n\nfunc Test_EncryptionDataConverterWorkflow(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\tenv := testSuite.NewTestWorkflowEnvironment()\n\tenv.RegisterWorkflow(EncryptionDataConverterWorkflow)\n\tenv.RegisterActivity(EncryptionDataConverterActivity)\n\n\tdataConverter, err := NewEncryptedJSONDataConverter(demoEncryptionKey)\n\trequire.NoError(t, err)\n\tworkerOptions := worker.Options{\n\t\tDataConverter: dataConverter,\n\t}\n\tenv.SetWorkerOptions(workerOptions)\n\n\tvar activityResult SensitiveCustomerRecord\n\tenv.SetOnActivityCompletedListener(func(activityInfo *activity.Info, result encoded.Value, err error) {\n\t\tresult.Get(&activityResult)\n\t})\n\n\t// Workflow generates its own payload internally, no input needed\n\tenv.ExecuteWorkflow(EncryptionDataConverterWorkflow)\n\n\trequire.True(t, env.IsWorkflowCompleted())\n\trequire.NoError(t, env.GetWorkflowError())\n\trequire.Equal(t, \"cust_8a7f3b2e\", activityResult.CustomerID)\n\trequire.Equal(t, \"workflow-processor-v2 (Encrypted)\", activityResult.ProcessedBy)\n}\n\nfunc Test_EncryptionRoundTrip(t *testing.T) {\n\tconverter, err := NewEncryptedJSONDataConverter(demoEncryptionKey)\n\trequire.NoError(t, err)\n\n\toriginal := CreateSensitiveCustomerRecord()\n\tencrypted, err := converter.ToData(original)\n\trequire.NoError(t, err)\n\trequire.NotEmpty(t, encrypted)\n\n\tvar decoded SensitiveCustomerRecord\n\terr = converter.FromData(encrypted, &decoded)\n\trequire.NoError(t, err)\n\trequire.Equal(t, original.SSN, decoded.SSN)\n\trequire.Equal(t, original.CreditCard, decoded.CreditCard)\n\trequire.Equal(t, original.MedicalNotes, decoded.MedicalNotes)\n}\n\nfunc Test_EncryptionDifferentEachTime(t *testing.T) {\n\tconverter, err := NewEncryptedJSONDataConverter(demoEncryptionKey)\n\trequire.NoError(t, err)\n\trecord := CreateSensitiveCustomerRecord()\n\n\tenc1, err := converter.ToData(record)\n\trequire.NoError(t, err)\n\tenc2, err := converter.ToData(record)\n\trequire.NoError(t, err)\n\n\t// Each encryption produces a different ciphertext due to random nonce\n\trequire.NotEqual(t, enc1, enc2)\n}\n\nfunc Test_NewEncryptedJSONDataConverter_InvalidKey(t *testing.T) {\n\t_, err := NewEncryptedJSONDataConverter([]byte(\"too-short\"))\n\trequire.Error(t, err)\n\trequire.ErrorIs(t, err, errFailedToCreateConverter)\n}\n"
  },
  {
    "path": "new_samples/data/generator/README.md",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# Sample Generator\n\nThis folder is NOT part of the actual sample. It exists only for contributors who work on this sample. Please disregard it if you are trying to learn about Cadence.\n\nTo create a better learning experience for Cadence users, each sample folder is designed to be self contained. Users can view every part of writing and running workflows, including:\n\n* Cadence client initialization\n* Worker with workflow and activity registrations\n* Workflow starter\n* and the workflow code itself\n\nSome samples may have more or fewer parts depending on what they need to demonstrate.\n\nIn most cases, the workflow code (e.g. `workflow.go`) is the part that users care about. The rest is boilerplate needed to run that workflow. For each sample folder, the workflow code should be written by hand. The boilerplate can be generated. Keeping all parts inside one folder gives early learners more value because they can see everything together rather than jumping across directories.\n\n## Contributing\n\n* When creating a new sample, follow the steps mentioned in the README file in the main samples folder.\n* To update the sample workflow code, edit the workflow file directly.\n* To update the worker, client, or other boilerplate logic, edit the generator file. If your change applies to all samples, update the common generator file inside the `template` folder. Edit the generator file in this folder only when the change should affect this sample alone.\n* When you are done run the following command in the generator folder\n\n```bash\ngo run .\n```\n"
  },
  {
    "path": "new_samples/data/generator/README_specific.md",
    "content": "## Data Converter Samples\n\nThis folder demonstrates three production-ready patterns for custom `DataConverter` implementations in Cadence. A `DataConverter` controls how every workflow input, output, and activity parameter is serialized before it is written to Cadence history — making it the right place to add compression, encryption, or external offloading without changing any workflow or activity code.\n\n### What is a DataConverter?\n\nA `DataConverter` implements two methods:\n\n- `ToData(value ...interface{}) ([]byte, error)` — called before data is written to Cadence history\n- `FromData(input []byte, valuePtr ...interface{}) error` — called when data is read back\n\nThe same `DataConverter` must be used by **both the worker and any client that triggers or queries the workflow**. In this sample the workflows generate their own payloads internally, so they can be started from the Cadence CLI without bundling a custom converter into the CLI itself.\n\nEach sample runs its own worker on its own task list so it can use its own `DataConverter`. Start all three with a single `go run .`.\n\n---\n\n### Compression Sample\n\n`CompressionDataConverterWorkflow` demonstrates gzip-over-JSON compression. For repetitive JSON data this typically achieves 60–80% size reduction, lowering storage costs and bandwidth for large workflow payloads.\n\n**Task list:** `cadence-samples-data-compression`\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.CompressionDataConverterWorkflow \\\n  --tl cadence-samples-data-compression \\\n  --et 60\n```\n\nWhen the worker starts it prints a compression statistics banner showing the before/after sizes of the sample payload so you can see the benefit immediately.\n\n---\n\n### Encryption Sample\n\n`EncryptionDataConverterWorkflow` demonstrates AES-256-GCM encryption. Every workflow input, output, and activity parameter is encrypted before being written to Cadence history. Without the key, the data stored by the Cadence server — including any operators browsing workflow history — is completely opaque.\n\nThe sample uses a `SensitiveCustomerRecord` containing realistic PII and PHI fields (name, email, SSN, credit card, medical notes) to make the use case concrete.\n\n**Task list:** `cadence-samples-data-encryption`\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.EncryptionDataConverterWorkflow \\\n  --tl cadence-samples-data-encryption \\\n  --et 60\n```\n\n#### Encryption key\n\nBy default, the worker uses a hardcoded demo key and prints a prominent warning. To use your own key:\n\n```bash\n# Generate a random 32-byte (256-bit) key\nexport CADENCE_ENCRYPTION_KEY=$(openssl rand -hex 32)\ngo run .\n```\n\n> **WARNING:** The hardcoded demo key (`cadence-demo-key-NOT-FOR-PROD!!!`) is public.\n> Never use it in production. In production, load your key from a secrets manager\n> (AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager, etc.).\n\n#### How AES-256-GCM works\n\n- `ToData`: JSON-encode arguments → generate a 12-byte random nonce → `cipher.AEAD.Seal` → return `nonce || ciphertext+tag`.\n- `FromData`: split nonce from input → `cipher.AEAD.Open` → JSON-decode.\n\nThe GCM authentication tag (16 bytes) ensures ciphertext tampering is detected. The random nonce means the same plaintext produces different ciphertext on every call, preventing replay detection by an attacker who observes Cadence history.\n\n---\n\n### S3 Offload Sample (Claim-Check Pattern)\n\n`S3OffloadDataConverterWorkflow` demonstrates the *claim-check* pattern: payloads larger than a configurable threshold are stored in an external `BlobStore` and only a small reference (a few dozen bytes) travels through Cadence workflow history.\n\nThis solves the practical problem of Cadence's per-payload size limits (~2 MB) for workflows that must pass very large datasets between the workflow and its activities.\n\n**Task list:** `cadence-samples-data-s3`\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.S3OffloadDataConverterWorkflow \\\n  --tl cadence-samples-data-s3 \\\n  --et 60\n```\n\n#### How it works\n\n- `ToData`: JSON-encode → if `len(json) > thresholdBytes`, upload to `BlobStore` under a SHA-256 key and return `0x01 || {\"__s3_ref\":\"<bucket>/<sha256hex>\"}`. Otherwise return `0x00 || json` inline.\n- `FromData`: read prefix byte → if `0x01`, fetch from `BlobStore` and decode; if `0x00`, decode inline.\n\n#### Default store (zero-config)\n\nOut of the box, `localFSBlobStore` writes blobs to `os.TempDir()/cadence-samples-data-s3/`. No cloud credentials or additional dependencies are needed.\n\n#### Swapping in real AWS S3\n\nThe file `s3_dataconverter_workflow.go` contains a commented `s3BlobStore` stub showing the exact AWS SDK calls needed. To enable it:\n\n1. Add the AWS SDK to your module:\n   ```bash\n   go get github.com/aws/aws-sdk-go-v2/config\n   go get github.com/aws/aws-sdk-go-v2/service/s3\n   ```\n2. Uncomment the `s3BlobStore` section in `s3_dataconverter_workflow.go`.\n3. Replace `NewLocalFSBlobStore()` with `NewS3BlobStore(bucket, region)` in `worker.go`.\n4. Set standard AWS environment variables (`AWS_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`) or use an IAM instance role.\n\nYou can also point the SDK at [LocalStack](https://localstack.cloud/) or [MinIO](https://min.io/) for local testing without a real AWS account.\n\n> **Note on cleanup:** The `s3OffloadDataConverter` does not delete blobs after the workflow completes. In production, use S3 object lifecycle policies to automatically expire old blobs.\n\n---\n\n### When to use which pattern\n\n| Pattern | Best for |\n|---------|----------|\n| **Compression** | Large repetitive JSON payloads; reducing storage cost without confidentiality requirements |\n| **Encryption** | PII, PHI, secrets, or any data that must be unreadable in Cadence history |\n| **S3 Offload** | Payloads approaching Cadence's size limits; binary or non-JSON data; cost-conscious archival |\n\nPatterns can be composed: encrypt-then-compress, or encrypt-then-offload to S3 for maximum security and minimum history size.\n"
  },
  {
    "path": "new_samples/data/generator/generate.go",
    "content": "package main\n\nimport \"github.com/uber-common/cadence-samples/new_samples/template\"\n\nfunc main() {\n\t// Define the data for Data samples.\n\t// NOTE: worker.go is hand-written (not generated) because each sample\n\t// requires its own DataConverter in worker options. We call the individual\n\t// generation functions explicitly instead of template.GenerateAll so the\n\t// hand-written worker.go is never clobbered.\n\tdata := template.TemplateData{\n\t\tSampleName: \"Data\",\n\t\tWorkflows: []string{\n\t\t\t\"CompressionDataConverterWorkflow\",\n\t\t\t\"EncryptionDataConverterWorkflow\",\n\t\t\t\"S3OffloadDataConverterWorkflow\",\n\t\t},\n\t\tActivities: []string{\n\t\t\t\"CompressionDataConverterActivity\",\n\t\t\t\"EncryptionDataConverterActivity\",\n\t\t\t\"S3OffloadDataConverterActivity\",\n\t\t},\n\t}\n\n\t// Explicitly skip GenerateWorker — worker.go is maintained by hand.\n\ttemplate.GenerateMain(data)\n\ttemplate.GenerateSampleReadMe(data)\n\ttemplate.GenerateGeneratorReadMe(data)\n}\n"
  },
  {
    "path": "new_samples/data/main.go",
    "content": "// THIS IS A GENERATED FILE\n// PLEASE DO NOT EDIT\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc main() {\n\tStartWorker()\n\n\tdone := make(chan os.Signal, 1)\n\tsignal.Notify(done, syscall.SIGINT)\n\tfmt.Println(\"Cadence worker started, press ctrl+c to terminate...\")\n\t<-done\n}\n"
  },
  {
    "path": "new_samples/data/s3_dataconverter_workflow.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n// BlobStore is an abstraction over any external object store (local filesystem, S3, GCS, etc.).\n// The s3OffloadDataConverter uses this interface to store large payloads outside Cadence history.\ntype BlobStore interface {\n\tPut(ctx context.Context, key string, data []byte) error\n\tGet(ctx context.Context, key string) ([]byte, error)\n}\n\n// localFSBlobStore implements BlobStore using the local filesystem.\n// It is the default zero-config implementation used when running this demo without real AWS.\n// Files are written under os.TempDir()/cadence-samples-data-s3/.\ntype localFSBlobStore struct {\n\tbaseDir string\n}\n\n// NewLocalFSBlobStore creates a local filesystem blob store under os.TempDir().\nfunc NewLocalFSBlobStore() BlobStore {\n\tbaseDir := filepath.Join(os.TempDir(), \"cadence-samples-data-s3\")\n\tif err := os.MkdirAll(baseDir, 0o755); err != nil {\n\t\tpanic(fmt.Sprintf(\"failed to create blob store dir %s: %v\", baseDir, err))\n\t}\n\treturn &localFSBlobStore{baseDir: baseDir}\n}\n\n// sanitizeKey turns a \"bucket/sha256hex\" key into a single safe filename. Keys are always\n// generated internally by the DataConverter, but filepath.Base provides a belt-and-suspenders\n// guarantee against directory traversal in case a future caller passes a user-controlled key.\nfunc sanitizeKey(key string) string {\n\treturn filepath.Base(strings.ReplaceAll(key, \"/\", \"_\"))\n}\n\nfunc (s *localFSBlobStore) Put(_ context.Context, key string, data []byte) error {\n\tpath := filepath.Join(s.baseDir, sanitizeKey(key))\n\treturn os.WriteFile(path, data, 0o644)\n}\n\nfunc (s *localFSBlobStore) Get(_ context.Context, key string) ([]byte, error) {\n\tpath := filepath.Join(s.baseDir, sanitizeKey(key))\n\treturn os.ReadFile(path)\n}\n\n// =============================================================================\n// S3 BlobStore stub\n//\n// To use a real AWS S3 bucket instead of the local filesystem:\n//  1. Add aws-sdk-go-v2 to go.mod:\n//       go get github.com/aws/aws-sdk-go-v2/config\n//       go get github.com/aws/aws-sdk-go-v2/service/s3\n//  2. Uncomment and compile the s3BlobStore implementation below.\n//  3. Replace NewLocalFSBlobStore() with NewS3BlobStore(bucket, region) in worker.go.\n//  4. Set AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY (or use an instance role).\n//\n// /*\n// import (\n//     \"github.com/aws/aws-sdk-go-v2/aws\"\n//     awsconfig \"github.com/aws/aws-sdk-go-v2/config\"\n//     \"github.com/aws/aws-sdk-go-v2/service/s3\"\n// )\n//\n// type s3BlobStore struct {\n//     client *s3.Client\n//     bucket string\n// }\n//\n// func NewS3BlobStore(bucket, region string) BlobStore {\n//     cfg, err := awsconfig.LoadDefaultConfig(context.Background(), awsconfig.WithRegion(region))\n//     if err != nil {\n//         panic(\"failed to load AWS config: \" + err.Error())\n//     }\n//     return &s3BlobStore{client: s3.NewFromConfig(cfg), bucket: bucket}\n// }\n//\n// func (s *s3BlobStore) Put(ctx context.Context, key string, data []byte) error {\n//     _, err := s.client.PutObject(ctx, &s3.PutObjectInput{\n//         Bucket: aws.String(s.bucket),\n//         Key:    aws.String(key),\n//         Body:   bytes.NewReader(data),\n//     })\n//     return err\n// }\n//\n// func (s *s3BlobStore) Get(ctx context.Context, key string) ([]byte, error) {\n//     out, err := s.client.GetObject(ctx, &s3.GetObjectInput{\n//         Bucket: aws.String(s.bucket),\n//         Key:    aws.String(key),\n//     })\n//     if err != nil {\n//         return nil, err\n//     }\n//     defer out.Body.Close()\n//     return io.ReadAll(out.Body)\n// }\n// */\n// =============================================================================\n\n// s3Envelope is the small reference stored in Cadence history when a payload is offloaded.\ntype s3Envelope struct {\n\tS3Ref string `json:\"__s3_ref\"`\n}\n\nconst (\n\t// inlinePrefix is prepended to inline (below-threshold) payloads so FromData can distinguish them.\n\tinlinePrefix = byte(0x00)\n\t// offloadPrefix is prepended to offloaded payloads.\n\toffloadPrefix = byte(0x01)\n\t// defaultThresholdBytes: payloads larger than this are offloaded to the BlobStore.\n\t// Cadence's default max payload size is ~2MB; this threshold is set intentionally low\n\t// so the demo workflow comfortably triggers offloading.\n\tdefaultThresholdBytes = 4096 // 4 KB\n)\n\n// s3OffloadDataConverter implements the claim-check pattern:\n// large payloads are stored in BlobStore; only a small reference travels through Cadence history.\ntype s3OffloadDataConverter struct {\n\tstore          BlobStore\n\tbucket         string\n\tthresholdBytes int\n}\n\n// NewS3OffloadDataConverter creates a new s3OffloadDataConverter.\n// store is the BlobStore backend (use NewLocalFSBlobStore() for zero-config demo).\n// bucket is a logical bucket/prefix name embedded in the reference key.\n// thresholdBytes is the max inline payload size; larger payloads are offloaded.\nfunc NewS3OffloadDataConverter(store BlobStore, bucket string, thresholdBytes int) encoded.DataConverter {\n\treturn &s3OffloadDataConverter{\n\t\tstore:          store,\n\t\tbucket:         bucket,\n\t\tthresholdBytes: thresholdBytes,\n\t}\n}\n\nfunc (dc *s3OffloadDataConverter) ToData(value ...interface{}) ([]byte, error) {\n\tvar jsonBuf bytes.Buffer\n\tenc := json.NewEncoder(&jsonBuf)\n\tfor i, obj := range value {\n\t\tif err := enc.Encode(obj); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to encode argument: %d, %v, with error: %v\", i, reflect.TypeOf(obj), err)\n\t\t}\n\t}\n\tjsonBytes := jsonBuf.Bytes()\n\n\tif len(jsonBytes) <= dc.thresholdBytes {\n\t\t// Small payload: store inline with a prefix marker\n\t\tresult := make([]byte, 1+len(jsonBytes))\n\t\tresult[0] = inlinePrefix\n\t\tcopy(result[1:], jsonBytes)\n\t\treturn result, nil\n\t}\n\n\t// Derive the key from the SHA-256 of the payload so ToData is idempotent across\n\t// Cadence workflow replays. Using uuid.New() here would write a new orphaned blob\n\t// on every replay because the SDK calls ToData again each time the workflow is\n\t// re-executed from the top. If the workflow needs to control the key (e.g. to\n\t// encode routing metadata), generate it with workflow.SideEffect and pass it\n\t// alongside the payload instead.\n\thash := sha256.Sum256(jsonBytes)\n\tkey := fmt.Sprintf(\"%s/%x\", dc.bucket, hash)\n\tif err := dc.store.Put(context.Background(), key, jsonBytes); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to offload payload to blob store (key=%s): %v\", key, err)\n\t}\n\n\tenvelope, err := json.Marshal(s3Envelope{S3Ref: key})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to marshal s3 envelope: %v\", err)\n\t}\n\n\tresult := make([]byte, 1+len(envelope))\n\tresult[0] = offloadPrefix\n\tcopy(result[1:], envelope)\n\treturn result, nil\n}\n\nfunc (dc *s3OffloadDataConverter) FromData(input []byte, valuePtr ...interface{}) error {\n\t// Empty input: workflow was started without arguments (e.g., from CLI without --input).\n\tif len(input) == 0 {\n\t\treturn nil\n\t}\n\n\tprefix, payload := input[0], input[1:]\n\n\tvar jsonData []byte\n\tswitch prefix {\n\tcase inlinePrefix:\n\t\t// Empty payload means zero arguments were encoded (e.g., no workflow input).\n\t\tif len(payload) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\tjsonData = payload\n\tcase offloadPrefix:\n\t\tvar envelope s3Envelope\n\t\tif err := json.Unmarshal(payload, &envelope); err != nil {\n\t\t\treturn fmt.Errorf(\"s3 offload: failed to unmarshal envelope: %v\", err)\n\t\t}\n\t\tfetched, err := dc.store.Get(context.Background(), envelope.S3Ref)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"s3 offload: failed to fetch payload from blob store (key=%s): %v\", envelope.S3Ref, err)\n\t\t}\n\t\tjsonData = fetched\n\tdefault:\n\t\treturn fmt.Errorf(\"s3 offload: unknown prefix byte 0x%02x\", prefix)\n\t}\n\n\tdec := json.NewDecoder(bytes.NewBuffer(jsonData))\n\tfor i, obj := range valuePtr {\n\t\tif err := dec.Decode(obj); err != nil {\n\t\t\treturn fmt.Errorf(\"unable to decode argument: %d, %v, with error: %v\", i, reflect.TypeOf(obj), err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// S3LargePayload is a sizable data structure used to demonstrate S3 offloading.\n// It is intentionally larger than defaultThresholdBytes so every workflow execution\n// triggers an offload to the BlobStore.\ntype S3LargePayload struct {\n\tJobID       string            `json:\"job_id\"`\n\tDescription string            `json:\"description\"`\n\tDataPoints  []S3DataPoint     `json:\"data_points\"`\n\tMetadata    map[string]string `json:\"metadata\"`\n\tProcessedBy string            `json:\"processed_by\"`\n}\n\n// S3DataPoint represents a single telemetry measurement.\ntype S3DataPoint struct {\n\tTimestamp string  `json:\"timestamp\"`\n\tMetric    string  `json:\"metric\"`\n\tValue     float64 `json:\"value\"`\n\tTags      string  `json:\"tags\"`\n}\n\n// CreateS3LargePayload creates a sample payload well above defaultThresholdBytes.\nfunc CreateS3LargePayload() S3LargePayload {\n\tpoints := make([]S3DataPoint, 200)\n\tfor i := range points {\n\t\tpoints[i] = S3DataPoint{\n\t\t\tTimestamp: fmt.Sprintf(\"2024-01-15T%02d:30:00Z\", i%24),\n\t\t\tMetric:    fmt.Sprintf(\"telemetry.sensor_%03d.temperature\", i),\n\t\t\tValue:     20.0 + float64(i%30)/10.0,\n\t\t\tTags:      fmt.Sprintf(\"region=us-east-1,host=node-%03d,env=production\", i%10),\n\t\t}\n\t}\n\n\tmeta := make(map[string]string)\n\tfor i := 0; i < 20; i++ {\n\t\tmeta[fmt.Sprintf(\"batch_key_%02d\", i)] = strings.Repeat(\"value-data-\", 5)\n\t}\n\n\treturn S3LargePayload{\n\t\tJobID:       \"batch-job-20240115-001\",\n\t\tDescription: strings.Repeat(\"Large telemetry batch job containing sensor readings from the production cluster. \", 10),\n\t\tDataPoints:  points,\n\t\tMetadata:    meta,\n\t\tProcessedBy: \"s3-offload-worker-v1\",\n\t}\n}\n\n// GetS3OffloadSizeInfo returns the JSON size, what is stored externally, and what travels through Cadence.\nfunc GetS3OffloadSizeInfo(payload S3LargePayload, thresholdBytes int) (int, int, error) {\n\tjsonData, err := json.Marshal(payload)\n\tif err != nil {\n\t\treturn 0, 0, fmt.Errorf(\"failed to marshal payload: %v\", err)\n\t}\n\tjsonSize := len(jsonData)\n\n\t// The Cadence history reference is: 1 prefix byte + JSON envelope {\"__s3_ref\":\"<bucket>/<sha256hex>\"}\n\t// A SHA-256 hex digest is 64 chars; bucket + \"/\" + hex ≈ bucket + 65 chars\n\tsampleEnvelope, _ := json.Marshal(s3Envelope{S3Ref: \"cadence-samples-data-s3/\" + strings.Repeat(\"a\", 64)})\n\tcadenceBytes := 1 + len(sampleEnvelope)\n\n\treturn jsonSize, cadenceBytes, nil\n}\n\n// S3OffloadDataConverterWorkflow demonstrates the claim-check pattern with a BlobStore.\n// Payloads larger than the threshold are stored externally; only a small reference is\n// kept in Cadence workflow history, dramatically reducing history storage requirements.\n//\n// Note: The workflow generates its own payload internally so it can be started from\n// the Cadence CLI without requiring the CLI to use the custom DataConverter.\nfunc S3OffloadDataConverterWorkflow(ctx workflow.Context) (S3LargePayload, error) {\n\tlogger := workflow.GetLogger(ctx)\n\n\tpayload := CreateS3LargePayload()\n\tlogger.Info(\"S3 offload workflow started\",\n\t\tzap.String(\"job_id\", payload.JobID),\n\t\tzap.Int(\"data_points\", len(payload.DataPoints)))\n\tlogger.Info(\"Large payload will be offloaded to BlobStore; only a reference travels through Cadence history\")\n\n\tactivityOptions := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, activityOptions)\n\n\tvar result S3LargePayload\n\terr := workflow.ExecuteActivity(ctx, S3OffloadDataConverterActivity, payload).Get(ctx, &result)\n\tif err != nil {\n\t\tlogger.Error(\"S3 offload workflow activity failed\", zap.Error(err))\n\t\treturn S3LargePayload{}, err\n\t}\n\n\tlogger.Info(\"S3 offload workflow completed\", zap.String(\"job_id\", result.JobID))\n\tlogger.Info(\"Note: Large payload was transparently offloaded and retrieved via the BlobStore\")\n\treturn result, nil\n}\n\n// S3OffloadDataConverterActivity processes the large payload retrieved from the BlobStore.\n// From the activity's perspective the DataConverter is invisible — it receives the full\n// deserialized struct just as it would with any other DataConverter.\nfunc S3OffloadDataConverterActivity(ctx context.Context, payload S3LargePayload) (S3LargePayload, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"S3 offload activity received payload\",\n\t\tzap.String(\"job_id\", payload.JobID),\n\t\tzap.Int(\"data_points\", len(payload.DataPoints)))\n\n\tpayload.ProcessedBy = payload.ProcessedBy + \" (Processed)\"\n\n\tlogger.Info(\"S3 offload activity completed\", zap.String(\"job_id\", payload.JobID))\n\treturn payload, nil\n}\n"
  },
  {
    "path": "new_samples/data/s3_dataconverter_workflow_test.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/encoded\"\n\t\"go.uber.org/cadence/testsuite\"\n\t\"go.uber.org/cadence/worker\"\n)\n\n// memoryBlobStore is an in-memory BlobStore used in tests to avoid filesystem I/O.\ntype memoryBlobStore struct {\n\tmu   sync.RWMutex\n\tdata map[string][]byte\n}\n\nfunc newMemoryBlobStore() BlobStore {\n\treturn &memoryBlobStore{data: make(map[string][]byte)}\n}\n\nfunc (m *memoryBlobStore) Put(_ context.Context, key string, data []byte) error {\n\tm.mu.Lock()\n\tdefer m.mu.Unlock()\n\tm.data[key] = append([]byte(nil), data...)\n\treturn nil\n}\n\nfunc (m *memoryBlobStore) Get(_ context.Context, key string) ([]byte, error) {\n\tm.mu.RLock()\n\tdefer m.mu.RUnlock()\n\td, ok := m.data[key]\n\tif !ok {\n\t\treturn nil, nil\n\t}\n\treturn append([]byte(nil), d...), nil\n}\n\nfunc Test_S3OffloadDataConverterWorkflow(t *testing.T) {\n\ttestSuite := &testsuite.WorkflowTestSuite{}\n\tenv := testSuite.NewTestWorkflowEnvironment()\n\tenv.RegisterWorkflow(S3OffloadDataConverterWorkflow)\n\tenv.RegisterActivity(S3OffloadDataConverterActivity)\n\n\tstore := newMemoryBlobStore()\n\tdataConverter := NewS3OffloadDataConverter(store, \"test-bucket\", defaultThresholdBytes)\n\tworkerOptions := worker.Options{\n\t\tDataConverter: dataConverter,\n\t}\n\tenv.SetWorkerOptions(workerOptions)\n\n\tvar activityResult S3LargePayload\n\tenv.SetOnActivityCompletedListener(func(activityInfo *activity.Info, result encoded.Value, err error) {\n\t\tresult.Get(&activityResult)\n\t})\n\n\t// Workflow generates its own payload internally, no input needed\n\tenv.ExecuteWorkflow(S3OffloadDataConverterWorkflow)\n\n\trequire.True(t, env.IsWorkflowCompleted())\n\trequire.NoError(t, env.GetWorkflowError())\n\trequire.Equal(t, \"batch-job-20240115-001\", activityResult.JobID)\n\trequire.Equal(t, \"s3-offload-worker-v1 (Processed)\", activityResult.ProcessedBy)\n\trequire.Equal(t, 200, len(activityResult.DataPoints))\n}\n\nfunc Test_S3OffloadRoundTrip(t *testing.T) {\n\tstore := newMemoryBlobStore()\n\tconverter := NewS3OffloadDataConverter(store, \"test-bucket\", defaultThresholdBytes)\n\n\toriginal := CreateS3LargePayload()\n\tencoded, err := converter.ToData(original)\n\trequire.NoError(t, err)\n\trequire.NotEmpty(t, encoded)\n\n\t// Large payload should be offloaded — the encoded form should be tiny\n\trequire.Equal(t, offloadPrefix, encoded[0], \"expected offload prefix for large payload\")\n\trequire.Less(t, len(encoded), 200, \"Cadence history reference should be much smaller than full payload\")\n\n\tvar decoded S3LargePayload\n\terr = converter.FromData(encoded, &decoded)\n\trequire.NoError(t, err)\n\trequire.Equal(t, original.JobID, decoded.JobID)\n\trequire.Equal(t, len(original.DataPoints), len(decoded.DataPoints))\n}\n\nfunc Test_S3ReplayIdempotent(t *testing.T) {\n\t// Simulate Cadence replay: ToData is called multiple times on the same payload.\n\t// Each call must produce an identical encoded output and write to the same blob key,\n\t// not create new orphaned blobs on every replay.\n\tstore := newMemoryBlobStore()\n\tconverter := NewS3OffloadDataConverter(store, \"test-bucket\", defaultThresholdBytes)\n\n\tpayload := CreateS3LargePayload()\n\n\tenc1, err := converter.ToData(payload)\n\trequire.NoError(t, err)\n\trequire.Equal(t, offloadPrefix, enc1[0], \"expected offload prefix for large payload\")\n\n\tenc2, err := converter.ToData(payload)\n\trequire.NoError(t, err)\n\n\t// Both calls must return byte-for-byte identical output (same key in the envelope).\n\trequire.Equal(t, enc1, enc2, \"ToData must be idempotent across replays — same payload must produce same encoded bytes\")\n\n\t// The store must contain exactly one entry, not two.\n\tmstore := store.(*memoryBlobStore)\n\tmstore.mu.RLock()\n\tblobCount := len(mstore.data)\n\tmstore.mu.RUnlock()\n\trequire.Equal(t, 1, blobCount, \"replayed ToData calls must overwrite the same blob key, not create new orphaned entries\")\n}\n\nfunc Test_S3InlineSmallPayload(t *testing.T) {\n\tstore := newMemoryBlobStore()\n\tconverter := NewS3OffloadDataConverter(store, \"test-bucket\", 100000) // very high threshold\n\n\toriginal := CreateS3LargePayload()\n\tenc, err := converter.ToData(original)\n\trequire.NoError(t, err)\n\trequire.Equal(t, inlinePrefix, enc[0], \"expected inline prefix when payload is under threshold\")\n\n\tvar decoded S3LargePayload\n\terr = converter.FromData(enc, &decoded)\n\trequire.NoError(t, err)\n\trequire.Equal(t, original.JobID, decoded.JobID)\n}\n"
  },
  {
    "path": "new_samples/data/worker.go",
    "content": "// Custom worker.go for Data samples - NOT generated\n// This sample requires custom DataConverters in worker options, one per sample.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/uber-go/tally\"\n\tapiv1 \"github.com/uber/cadence-idl/go/proto/api/v1\"\n\t\"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/compatibility\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/yarpc\"\n\t\"go.uber.org/yarpc/peer\"\n\tyarpchostport \"go.uber.org/yarpc/peer/hostport\"\n\t\"go.uber.org/yarpc/transport/grpc\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nconst (\n\tHostPort       = \"127.0.0.1:7833\"\n\tDomain         = \"cadence-samples\"\n\tClientName     = \"cadence-samples-worker\"\n\tCadenceService = \"cadence-frontend\"\n\n\t// Each sample uses its own task list so it can have its own DataConverter.\n\tTaskListCompression = \"cadence-samples-data-compression\"\n\tTaskListEncryption  = \"cadence-samples-data-encryption\"\n\tTaskListS3          = \"cadence-samples-data-s3\"\n)\n\n// StartWorker starts one worker per DataConverter sample and prints startup stats for each.\nfunc StartWorker() {\n\tlogger := BuildLogger()\n\tcadenceClient := BuildCadenceClient()\n\n\tstartCompressionWorker(logger, cadenceClient)\n\tstartEncryptionWorker(logger, cadenceClient)\n\tstartS3OffloadWorker(logger, cadenceClient)\n\n\tprintCompressionStats()\n\tprintEncryptionStats()\n\tprintS3OffloadStats()\n}\n\nfunc startCompressionWorker(logger *zap.Logger, cadenceClient workflowserviceclient.Interface) {\n\tdataConverter := NewCompressedJSONDataConverter()\n\tworkerOptions := worker.Options{\n\t\tLogger:        logger,\n\t\tMetricsScope:  tally.NewTestScope(TaskListCompression, nil),\n\t\tDataConverter: dataConverter,\n\t}\n\n\tw := worker.New(cadenceClient, Domain, TaskListCompression, workerOptions)\n\tw.RegisterWorkflowWithOptions(CompressionDataConverterWorkflow, workflow.RegisterOptions{Name: \"cadence_samples.CompressionDataConverterWorkflow\"})\n\tw.RegisterActivityWithOptions(CompressionDataConverterActivity, activity.RegisterOptions{Name: \"cadence_samples.CompressionDataConverterActivity\"})\n\n\tif err := w.Start(); err != nil {\n\t\tpanic(\"Failed to start compression worker: \" + err.Error())\n\t}\n\tlogger.Info(\"Started compression worker\", zap.String(\"task_list\", TaskListCompression))\n}\n\nfunc startEncryptionWorker(logger *zap.Logger, cadenceClient workflowserviceclient.Interface) {\n\tkey := LoadEncryptionKey()\n\tdataConverter, err := NewEncryptedJSONDataConverter(key)\n\tif err != nil {\n\t\tpanic(\"Failed to create encryption data converter: \" + err.Error())\n\t}\n\tworkerOptions := worker.Options{\n\t\tLogger:        logger,\n\t\tMetricsScope:  tally.NewTestScope(TaskListEncryption, nil),\n\t\tDataConverter: dataConverter,\n\t}\n\n\tw := worker.New(cadenceClient, Domain, TaskListEncryption, workerOptions)\n\tw.RegisterWorkflowWithOptions(EncryptionDataConverterWorkflow, workflow.RegisterOptions{Name: \"cadence_samples.EncryptionDataConverterWorkflow\"})\n\tw.RegisterActivityWithOptions(EncryptionDataConverterActivity, activity.RegisterOptions{Name: \"cadence_samples.EncryptionDataConverterActivity\"})\n\n\tif err := w.Start(); err != nil {\n\t\tpanic(\"Failed to start encryption worker: \" + err.Error())\n\t}\n\tlogger.Info(\"Started encryption worker\", zap.String(\"task_list\", TaskListEncryption))\n}\n\nfunc startS3OffloadWorker(logger *zap.Logger, cadenceClient workflowserviceclient.Interface) {\n\tstore := NewLocalFSBlobStore()\n\tdataConverter := NewS3OffloadDataConverter(store, \"cadence-samples-data-s3\", defaultThresholdBytes)\n\tworkerOptions := worker.Options{\n\t\tLogger:        logger,\n\t\tMetricsScope:  tally.NewTestScope(TaskListS3, nil),\n\t\tDataConverter: dataConverter,\n\t}\n\n\tw := worker.New(cadenceClient, Domain, TaskListS3, workerOptions)\n\tw.RegisterWorkflowWithOptions(S3OffloadDataConverterWorkflow, workflow.RegisterOptions{Name: \"cadence_samples.S3OffloadDataConverterWorkflow\"})\n\tw.RegisterActivityWithOptions(S3OffloadDataConverterActivity, activity.RegisterOptions{Name: \"cadence_samples.S3OffloadDataConverterActivity\"})\n\n\tif err := w.Start(); err != nil {\n\t\tpanic(\"Failed to start S3 offload worker: \" + err.Error())\n\t}\n\tlogger.Info(\"Started S3 offload worker\", zap.String(\"task_list\", TaskListS3))\n}\n\n// printCompressionStats displays gzip compression statistics for the sample payload.\nfunc printCompressionStats() {\n\tlargePayload := CreateLargePayload()\n\toriginalSize, compressedSize, compressionPercentage, err := GetPayloadSizeInfo(largePayload, NewCompressedJSONDataConverter())\n\tif err != nil {\n\t\tfmt.Printf(\"Error calculating compression stats: %v\\n\", err)\n\t\treturn\n\t}\n\n\tfmt.Printf(\"\\n=== Compression Sample Statistics ===\\n\")\n\tfmt.Printf(\"Original JSON size:  %d bytes (%.2f KB)\\n\", originalSize, float64(originalSize)/1024.0)\n\tfmt.Printf(\"Compressed size:     %d bytes (%.2f KB)\\n\", compressedSize, float64(compressedSize)/1024.0)\n\tfmt.Printf(\"Compression ratio:   %.2f%% reduction\\n\", compressionPercentage)\n\tfmt.Printf(\"Space saved:         %d bytes (%.2f KB)\\n\", originalSize-compressedSize, float64(originalSize-compressedSize)/1024.0)\n\tfmt.Printf(\"Start workflow: cadence --domain %s workflow start --tl %s --workflow_type cadence_samples.CompressionDataConverterWorkflow --et 60\\n\", Domain, TaskListCompression)\n\tfmt.Printf(\"=====================================\\n\\n\")\n}\n\n// printEncryptionStats displays AES-256-GCM encryption statistics for the sample record.\nfunc printEncryptionStats() {\n\trecord := CreateSensitiveCustomerRecord()\n\tconverter, err := NewEncryptedJSONDataConverter(demoEncryptionKey)\n\tif err != nil {\n\t\tfmt.Printf(\"Error creating encryption converter for stats: %v\\n\", err)\n\t\treturn\n\t}\n\tplaintextSize, ciphertextSize, preview, err := GetEncryptionSizeInfo(record, converter)\n\tif err != nil {\n\t\tfmt.Printf(\"Error calculating encryption stats: %v\\n\", err)\n\t\treturn\n\t}\n\n\tfmt.Printf(\"\\n=== Encryption Sample Statistics ===\\n\")\n\tfmt.Printf(\"Plaintext JSON size:  %d bytes\\n\", plaintextSize)\n\tfmt.Printf(\"Ciphertext size:      %d bytes (overhead: %d bytes nonce+tag)\\n\", ciphertextSize, ciphertextSize-plaintextSize)\n\tfmt.Printf(\"Ciphertext preview:   %s\\n\", preview)\n\tfmt.Printf(\"Start workflow: cadence --domain %s workflow start --tl %s --workflow_type cadence_samples.EncryptionDataConverterWorkflow --et 60\\n\", Domain, TaskListEncryption)\n\tfmt.Printf(\"====================================\\n\\n\")\n}\n\n// printS3OffloadStats displays claim-check offload statistics for the sample payload.\nfunc printS3OffloadStats() {\n\tpayload := CreateS3LargePayload()\n\tjsonSize, cadenceBytes, err := GetS3OffloadSizeInfo(payload, defaultThresholdBytes)\n\tif err != nil {\n\t\tfmt.Printf(\"Error calculating S3 offload stats: %v\\n\", err)\n\t\treturn\n\t}\n\n\tfmt.Printf(\"\\n=== S3 Offload Sample Statistics ===\\n\")\n\tfmt.Printf(\"Full payload JSON size:    %d bytes (%.2f KB)\\n\", jsonSize, float64(jsonSize)/1024.0)\n\tfmt.Printf(\"Stored in BlobStore:       %d bytes (%.2f KB)\\n\", jsonSize, float64(jsonSize)/1024.0)\n\tfmt.Printf(\"Stored in Cadence history: %d bytes (claim-check reference only)\\n\", cadenceBytes)\n\tfmt.Printf(\"Reduction in Cadence:      %.1f%%\\n\", 100.0*(1.0-float64(cadenceBytes)/float64(jsonSize)))\n\tfmt.Printf(\"BlobStore location:        %s/cadence-samples-data-s3/\\n\", os.TempDir())\n\tfmt.Printf(\"Start workflow: cadence --domain %s workflow start --tl %s --workflow_type cadence_samples.S3OffloadDataConverterWorkflow --et 60\\n\", Domain, TaskListS3)\n\tfmt.Printf(\"=====================================\\n\\n\")\n}\n\nfunc BuildCadenceClient(dialOptions ...grpc.DialOption) workflowserviceclient.Interface {\n\tgrpcTransport := grpc.NewTransport()\n\tmyChooser := peer.NewSingle(\n\t\tyarpchostport.Identify(HostPort),\n\t\tgrpcTransport.NewDialer(dialOptions...),\n\t)\n\toutbound := grpcTransport.NewOutbound(myChooser)\n\n\tdispatcher := yarpc.NewDispatcher(yarpc.Config{\n\t\tName: ClientName,\n\t\tOutbounds: yarpc.Outbounds{\n\t\t\tCadenceService: {Unary: outbound},\n\t\t},\n\t})\n\tif err := dispatcher.Start(); err != nil {\n\t\tpanic(\"Failed to start dispatcher: \" + err.Error())\n\t}\n\n\tclientConfig := dispatcher.ClientConfig(CadenceService)\n\n\treturn compatibility.NewThrift2ProtoAdapter(\n\t\tapiv1.NewDomainAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkflowAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkerAPIYARPCClient(clientConfig),\n\t\tapiv1.NewVisibilityAPIYARPCClient(clientConfig),\n\t)\n}\n\nfunc BuildLogger() *zap.Logger {\n\tconfig := zap.NewDevelopmentConfig()\n\tconfig.Level.SetLevel(zapcore.InfoLevel)\n\n\tvar err error\n\tlogger, err := config.Build()\n\tif err != nil {\n\t\tpanic(\"Failed to setup logger: \" + err.Error())\n\t}\n\n\treturn logger\n}\n"
  },
  {
    "path": "new_samples/hello_world/README.md",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# Hello World Sample\n\n## Prerequisites\n\n0. Install Cadence CLI. See instruction [here](https://cadenceworkflow.io/docs/cli/).\n1. Run the Cadence server:\n    1. Clone the [Cadence](https://github.com/cadence-workflow/cadence) repository if you haven't done already: `git clone https://github.com/cadence-workflow/cadence.git`\n    2. Run `docker compose -f docker/docker-compose.yml up` to start Cadence server\n    3. See more details at https://github.com/uber/cadence/blob/master/README.md\n2. Once everything is up and running in Docker, open [localhost:8088](localhost:8088) to view Cadence UI.\n3. Register the `cadence-samples` domain:\n\n```bash\ncadence --domain cadence-samples domain register\n```\n\nRefresh the [domains page](http://localhost:8088/domains) from step 2 to verify `cadence-samples` is registered.\n\n## Steps to run sample\n\nInside the folder this sample is defined, run the following command:\n\n```bash\ngo run .\n```\n\nThis will call the main function in main.go which starts the worker, which will be execute the sample workflow code\n\n### Start your workflow\n\nThis workflow takes an input message and greet you as response. Try the following CLI\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.HelloWorldWorkflow \\\n  --tl cadence-samples-worker \\\n  --et 60 \\\n  --input '{\"message\":\"Cadence\"}'\n```\n\nYou should see output like this:\n\n![Trigger command output](images/02-trigger-command-started-workflow.png)\n\nAnd the worker will log the completed workflow:\n\n![Worker output showing workflow completed](images/01-worker-output-workflow-completed.png)\n\nHere are the details to this command:\n\n* `--domain` option describes under which domain to run this workflow\n* `--workflow_type` option describes which workflow to execute\n* `-tl` (or `--tasklist`) tells cadence-server which tasklist to schedule tasks with. This is the same tasklist the worker polls tasks from. See worker.go\n* `--et` (or `--execution_timeout`) tells cadence server how long to wait until timing out the workflow\n* `--input` is the input to your workflow\n\nTo see more options run `cadence --help`\n\n### View your workflow\n\n#### Cadence UI (cadence-web)\n\nClick on `cadence-samples` domain in cadence-web to view your workflow.\n\n![Workflow list showing completed workflow](images/03-web-ui-workflow-list-completed.png)\n\nClick on the workflow to see details:\n\n* In Summary tab, you will see the input and output to your workflow\n\n![Summary tab](images/04-web-ui-summary-tab.png)\n\n* Click on History tab to see individual steps. Expand an activity to see its result:\n\n![History tab with activity result](images/05-web-ui-history-activity-result.png)\n\n* In Summary tab, you will see the input and output to your workflow\n* Click on History tab to see individual steps.\n\n#### CLI\n\nList workflows using the following command:\n\n```bash\ncadence --domain cadence-samples workflow list\n```\n\nYou can view an individual workflow by using the following command:\n\n```bash\ncadence --domain cadence-samples \\\n  workflow describe \\\n  --wid <workflow_id>\n```\n\n* `workflow` is the noun to run commands within workflow scope\n* `describe` is the verb to return the summary of the workflow\n* `--wid` (or `--workflow_id`) is the option to pass the workflow id. If there are multiple \"run\"s, it will return the latest one.\n* (optional) `--rid` (or `--run_id`) is the option to pass the run id to describe a specific run, instead of the latest.\n\nTo view the entire history of the workflow, use the following command:\n\n```bash\ncadence --domain cadence-samples \\\n  workflow show \\\n  --wid <workflow_id>\n```\n\n## Troubleshooting\n\nIf you see port conflicts when starting Docker, use `lsof` to find what's using the port:\n\n![Docker port conflict troubleshooting](images/06-docker-port-conflict-troubleshooting.png)\n\nSee the main [README](../../README.md#docker-troubleshooting) for detailed Docker troubleshooting steps.\n## References\n\n* The website: https://cadenceworkflow.io\n* Cadence's server: https://github.com/uber/cadence\n* Cadence's Go client: https://github.com/uber-go/cadence-client\n\n"
  },
  {
    "path": "new_samples/hello_world/generator/README.md",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# Sample Generator\n\nThis folder is NOT part of the actual sample. It exists only for contributors who work on this sample. Please disregard it if you are trying to learn about Cadence.\n\nTo create a better learning experience for Cadence users, each sample folder is designed to be self contained. Users can view every part of writing and running workflows, including:\n\n* Cadence client initialization\n* Worker with workflow and activity registrations\n* Workflow starter\n* and the workflow code itself\n\nSome samples may have more or fewer parts depending on what they need to demonstrate.\n\nIn most cases, the workflow code (e.g. `workflow.go`) is the part that users care about. The rest is boilerplate needed to run that workflow. For each sample folder, the workflow code should be written by hand. The boilerplate can be generated. Keeping all parts inside one folder gives early learners more value because they can see everything together rather than jumping across directories.\n\n## Contributing\n\n* When creating a new sample, follow the steps mentioned in the README file in the main samples folder.\n* To update the sample workflow code, edit the workflow file directly.\n* To update the worker, client, or other boilerplate logic, edit the generator file. If your change applies to all samples, update the common generator file inside the `template` folder. Edit the generator file in this folder only when the change should affect this sample alone.\n* When you are done run the following command in the generator folder\n\n```bash\ngo run .\n```\n"
  },
  {
    "path": "new_samples/hello_world/generator/README_specific.md",
    "content": "### Start your workflow\n\nThis workflow takes an input message and greet you as response. Try the following CLI\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.HelloWorldWorkflow \\\n  --tl cadence-samples-worker \\\n  --et 60 \\\n  --input '{\"message\":\"Cadence\"}'\n```\n\nYou should see output like this:\n\n![Trigger command output](images/02-trigger-command-started-workflow.png)\n\nAnd the worker will log the completed workflow:\n\n![Worker output showing workflow completed](images/01-worker-output-workflow-completed.png)\n\nHere are the details to this command:\n\n* `--domain` option describes under which domain to run this workflow\n* `--workflow_type` option describes which workflow to execute\n* `-tl` (or `--tasklist`) tells cadence-server which tasklist to schedule tasks with. This is the same tasklist the worker polls tasks from. See worker.go\n* `--et` (or `--execution_timeout`) tells cadence server how long to wait until timing out the workflow\n* `--input` is the input to your workflow\n\nTo see more options run `cadence --help`\n\n### View your workflow\n\n#### Cadence UI (cadence-web)\n\nClick on `cadence-samples` domain in cadence-web to view your workflow.\n\n![Workflow list showing completed workflow](images/03-web-ui-workflow-list-completed.png)\n\nClick on the workflow to see details:\n\n* In Summary tab, you will see the input and output to your workflow\n\n![Summary tab](images/04-web-ui-summary-tab.png)\n\n* Click on History tab to see individual steps. Expand an activity to see its result:\n\n![History tab with activity result](images/05-web-ui-history-activity-result.png)\n\n* In Summary tab, you will see the input and output to your workflow\n* Click on History tab to see individual steps.\n\n#### CLI\n\nList workflows using the following command:\n\n```bash\ncadence --domain cadence-samples workflow list\n```\n\nYou can view an individual workflow by using the following command:\n\n```bash\ncadence --domain cadence-samples \\\n  workflow describe \\\n  --wid <workflow_id>\n```\n\n* `workflow` is the noun to run commands within workflow scope\n* `describe` is the verb to return the summary of the workflow\n* `--wid` (or `--workflow_id`) is the option to pass the workflow id. If there are multiple \"run\"s, it will return the latest one.\n* (optional) `--rid` (or `--run_id`) is the option to pass the run id to describe a specific run, instead of the latest.\n\nTo view the entire history of the workflow, use the following command:\n\n```bash\ncadence --domain cadence-samples \\\n  workflow show \\\n  --wid <workflow_id>\n```\n\n## Troubleshooting\n\nIf you see port conflicts when starting Docker, use `lsof` to find what's using the port:\n\n![Docker port conflict troubleshooting](images/06-docker-port-conflict-troubleshooting.png)\n\nSee the main [README](../../README.md#docker-troubleshooting) for detailed Docker troubleshooting steps."
  },
  {
    "path": "new_samples/hello_world/generator/generate.go",
    "content": "package main\n\nimport \"github.com/uber-common/cadence-samples/new_samples/template\"\n\nfunc main() {\n\t// Define the data for HelloWorld\n\tdata := template.TemplateData{\n\t\tSampleName: \"Hello World\",\n\t\tWorkflows:  []string{\"HelloWorldWorkflow\"},\n\t\tActivities: []string{\"HelloWorldActivity\"},\n\t}\n\n\ttemplate.GenerateAll(data)\n}\n\n// Implement custom generator below"
  },
  {
    "path": "new_samples/hello_world/main.go",
    "content": "// THIS IS A GENERATED FILE\n// PLEASE DO NOT EDIT\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc main() {\n\tStartWorker()\n\n\tdone := make(chan os.Signal, 1)\n\tsignal.Notify(done, syscall.SIGINT)\n\tfmt.Println(\"Cadence worker started, press ctrl+c to terminate...\")\n\t<-done\n}\n"
  },
  {
    "path": "new_samples/hello_world/worker.go",
    "content": "// THIS IS A GENERATED FILE\n// PLEASE DO NOT EDIT\n\n// Package worker implements a Cadence worker with basic configurations.\npackage main\n\nimport (\n\t\"github.com/uber-go/tally\"\n\tapiv1 \"github.com/uber/cadence-idl/go/proto/api/v1\"\n\t\"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/compatibility\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/yarpc\"\n\t\"go.uber.org/yarpc/peer\"\n\tyarpchostport \"go.uber.org/yarpc/peer/hostport\"\n\t\"go.uber.org/yarpc/transport/grpc\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nconst (\n\tHostPort = \"127.0.0.1:7833\"\n\tDomain   = \"cadence-samples\"\n\t// TaskListName identifies set of client workflows, activities, and workers.\n\t// It could be your group or client or application name.\n\tTaskListName   = \"cadence-samples-worker\"\n\tClientName     = \"cadence-samples-worker\"\n\tCadenceService = \"cadence-frontend\"\n)\n\n// StartWorker creates and starts a basic Cadence worker.\nfunc StartWorker() {\n\tlogger, cadenceClient := BuildLogger(), BuildCadenceClient()\n\tworkerOptions := worker.Options{\n\t\tLogger:       logger,\n\t\tMetricsScope: tally.NewTestScope(TaskListName, nil),\n\t}\n\n\tw := worker.New(\n\t\tcadenceClient,\n\t\tDomain,\n\t\tTaskListName,\n\t\tworkerOptions)\n\t// HelloWorld workflow registration\n\tw.RegisterWorkflowWithOptions(HelloWorldWorkflow, workflow.RegisterOptions{Name: \"cadence_samples.HelloWorldWorkflow\"})\n\tw.RegisterActivityWithOptions(HelloWorldActivity, activity.RegisterOptions{Name: \"cadence_samples.HelloWorldActivity\"})\n\n\terr := w.Start()\n\tif err != nil {\n\t\tpanic(\"Failed to start worker: \" + err.Error())\n\t}\n\tlogger.Info(\"Started Worker.\", zap.String(\"worker\", TaskListName))\n\n}\n\nfunc BuildCadenceClient(dialOptions ...grpc.DialOption) workflowserviceclient.Interface {\n\tgrpcTransport := grpc.NewTransport()\n\t// Create a single peer chooser that identifies the host/port and configures\n\t// a gRPC dialer with TLS credentials\n\tmyChooser := peer.NewSingle(\n\t\tyarpchostport.Identify(HostPort),\n\t\tgrpcTransport.NewDialer(dialOptions...),\n\t)\n\toutbound := grpcTransport.NewOutbound(myChooser)\n\n\tdispatcher := yarpc.NewDispatcher(yarpc.Config{\n\t\tName: ClientName,\n\t\tOutbounds: yarpc.Outbounds{\n\t\t\tCadenceService: {Unary: outbound},\n\t\t},\n\t})\n\tif err := dispatcher.Start(); err != nil {\n\t\tpanic(\"Failed to start dispatcher: \" + err.Error())\n\t}\n\n\tclientConfig := dispatcher.ClientConfig(CadenceService)\n\n\t// Create a compatibility adapter that wraps proto-based YARPC clients\n\t// to provide a unified interface for domain, workflow, worker, and visibility APIs\n\treturn compatibility.NewThrift2ProtoAdapter(\n\t\tapiv1.NewDomainAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkflowAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkerAPIYARPCClient(clientConfig),\n\t\tapiv1.NewVisibilityAPIYARPCClient(clientConfig),\n\t)\n}\n\nfunc BuildLogger() *zap.Logger {\n\tconfig := zap.NewDevelopmentConfig()\n\tconfig.Level.SetLevel(zapcore.InfoLevel)\n\n\tvar err error\n\tlogger, err := config.Build()\n\tif err != nil {\n\t\tpanic(\"Failed to setup logger: \" + err.Error())\n\t}\n\n\treturn logger\n}\n"
  },
  {
    "path": "new_samples/hello_world/workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n\t\"time\"\n)\n\ntype sampleInput struct {\n\tMessage string `json:\"message\"`\n}\n\n// This is the workflow function\n// Given an input, HelloWorldWorkflow returns \"Hello <input>!\" \nfunc HelloWorldWorkflow(ctx workflow.Context, input sampleInput) (string, error) {\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"HelloWorldWorkflow started\")\n\n\tvar greetingMsg string\n\terr := workflow.ExecuteActivity(ctx, HelloWorldActivity, input).Get(ctx, &greetingMsg)\n\tif err != nil {\n\t\tlogger.Error(\"HelloWorldActivity failed\", zap.Error(err))\n\t\treturn \"\", err\n\t}\n\n\tlogger.Info(\"Workflow result\", zap.String(\"greeting\", greetingMsg))\n\treturn greetingMsg, nil\n}\n\n// This is the activity function\n// Given an input, HelloWorldActivity returns \"Hello <input>!\"\nfunc HelloWorldActivity(ctx context.Context, input sampleInput) (string, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"HelloWorldActivity started\")\n\treturn fmt.Sprintf(\"Hello, %s!\", input.Message), nil\n}\n"
  },
  {
    "path": "new_samples/operations/README.md",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# Operations Sample\n\n## Prerequisites\n\n0. Install Cadence CLI. See instruction [here](https://cadenceworkflow.io/docs/cli/).\n1. Run the Cadence server:\n    1. Clone the [Cadence](https://github.com/cadence-workflow/cadence) repository if you haven't done already: `git clone https://github.com/cadence-workflow/cadence.git`\n    2. Run `docker compose -f docker/docker-compose.yml up` to start Cadence server\n    3. See more details at https://github.com/uber/cadence/blob/master/README.md\n2. Once everything is up and running in Docker, open [localhost:8088](localhost:8088) to view Cadence UI.\n3. Register the `cadence-samples` domain:\n\n```bash\ncadence --domain cadence-samples domain register\n```\n\nRefresh the [domains page](http://localhost:8088/domains) from step 2 to verify `cadence-samples` is registered.\n\n## Steps to run sample\n\nInside the folder this sample is defined, run the following command:\n\n```bash\ngo run .\n```\n\nThis will call the main function in main.go which starts the worker, which will be execute the sample workflow code\n\n## Samples in this folder\n\nThis folder contains samples demonstrating workflow operations and lifecycle management in Cadence.\n\n### Cancel Workflow\n\nThe `CancelWorkflow` demonstrates how to properly handle workflow cancellation, including:\n- Graceful cleanup when a workflow is cancelled\n- Using a disconnected context to run cleanup activities after cancellation\n- Heartbeating in long-running activities to detect cancellation\n\n#### Start the workflow\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.CancelWorkflow \\\n  --tl cadence-samples-worker \\\n  --et 600 \\\n  --input '{}'\n```\n\nCopy the workflow ID from the output.\n\n#### Cancel the workflow\n\n```bash\ncadence --domain cadence-samples \\\n  workflow cancel \\\n  --workflow_id <YOUR_WORKFLOW_ID>\n```\n\n#### What to observe\n\nAfter cancellation:\n1. The `ActivityToBeCanceled` will detect the cancellation via `ctx.Done()` and return\n2. The `ActivityToBeSkipped` will not be scheduled (context already cancelled)\n3. The `CleanupActivity` will run using a disconnected context to perform cleanup\n\nThis pattern is essential for workflows that need to release resources or perform cleanup operations when cancelled.\n\n## References\n\n* The website: https://cadenceworkflow.io\n* Cadence's server: https://github.com/uber/cadence\n* Cadence's Go client: https://github.com/uber-go/cadence-client\n\n"
  },
  {
    "path": "new_samples/operations/cancel_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"go.uber.org/cadence\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n\t\"time\"\n)\n\nfunc CancelWorkflow(ctx workflow.Context) (retError error) {\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute,\n\t\tStartToCloseTimeout:    time.Minute * 30,\n\t\tHeartbeatTimeout:       time.Second * 5,\n\t\tWaitForCancellation:    true,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"cancel workflow started\")\n\n\tdefer func() {\n\t\tif cadence.IsCanceledError(retError) {\n\t\t\t// When workflow is canceled, it has to get a new disconnected context to execute any activities\n\t\t\tnewCtx, _ := workflow.NewDisconnectedContext(ctx)\n\t\t\terr := workflow.ExecuteActivity(newCtx, CleanupActivity).Get(ctx, nil)\n\t\t\tif err != nil {\n\t\t\t\tlogger.Error(\"Cleanup activity failed\", zap.Error(err))\n\t\t\t\tretError = err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tretError = nil\n\t\t\tlogger.Info(\"Workflow completed.\")\n\t\t}\n\t}()\n\n\tvar result string\n\terr := workflow.ExecuteActivity(ctx, ActivityToBeCanceled).Get(ctx, &result)\n\tif err != nil && !cadence.IsCanceledError(err) {\n\t\tlogger.Error(\"Error from activityToBeCanceled\", zap.Error(err))\n\t\treturn err\n\t}\n\tlogger.Info(fmt.Sprintf(\"activityToBeCanceled returns %v, %v\", result, err))\n\n\t// Execute activity using a canceled ctx,\n\t// activity won't be scheduled and a cancelled error will be returned\n\terr = workflow.ExecuteActivity(ctx, ActivityToBeSkipped).Get(ctx, nil)\n\tif err != nil && !cadence.IsCanceledError(err) {\n\t\tlogger.Error(\"Error from activityToBeSkipped\", zap.Error(err))\n\t}\n\n\treturn err\n}\n\nfunc ActivityToBeCanceled(ctx context.Context) (string, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"activity started, to cancel workflow, use CLI: 'cadence --do default wf cancel -w <WorkflowID>' to cancel\")\n\tfor {\n\t\tselect {\n\t\tcase <-time.After(1 * time.Second):\n\t\t\tlogger.Info(\"heart beating...\")\n\t\t\tactivity.RecordHeartbeat(ctx, \"\")\n\t\tcase <-ctx.Done():\n\t\t\tlogger.Info(\"context is cancelled\")\n\t\t\t// returned canceled error here so that in workflow history we can see ActivityTaskCanceled event\n\t\t\t// or if not cancelled, return timeout error\n\t\t\treturn \"I am canceled by Done\", ctx.Err()\n\t\t}\n\t}\n}\n\nfunc CleanupActivity(ctx context.Context) error {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"cleanupActivity started\")\n\treturn nil\n}\n\nfunc ActivityToBeSkipped(ctx context.Context) error {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"this activity will be skipped due to cancellation\")\n\treturn nil\n}\n"
  },
  {
    "path": "new_samples/operations/generator/README.md",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# Sample Generator\n\nThis folder is NOT part of the actual sample. It exists only for contributors who work on this sample. Please disregard it if you are trying to learn about Cadence.\n\nTo create a better learning experience for Cadence users, each sample folder is designed to be self contained. Users can view every part of writing and running workflows, including:\n\n* Cadence client initialization\n* Worker with workflow and activity registrations\n* Workflow starter\n* and the workflow code itself\n\nSome samples may have more or fewer parts depending on what they need to demonstrate.\n\nIn most cases, the workflow code (e.g. `workflow.go`) is the part that users care about. The rest is boilerplate needed to run that workflow. For each sample folder, the workflow code should be written by hand. The boilerplate can be generated. Keeping all parts inside one folder gives early learners more value because they can see everything together rather than jumping across directories.\n\n## Contributing\n\n* When creating a new sample, follow the steps mentioned in the README file in the main samples folder.\n* To update the sample workflow code, edit the workflow file directly.\n* To update the worker, client, or other boilerplate logic, edit the generator file. If your change applies to all samples, update the common generator file inside the `template` folder. Edit the generator file in this folder only when the change should affect this sample alone.\n* When you are done run the following command in the generator folder\n\n```bash\ngo run .\n```\n"
  },
  {
    "path": "new_samples/operations/generator/README_specific.md",
    "content": "## Samples in this folder\n\nThis folder contains samples demonstrating workflow operations and lifecycle management in Cadence.\n\n### Cancel Workflow\n\nThe `CancelWorkflow` demonstrates how to properly handle workflow cancellation, including:\n- Graceful cleanup when a workflow is cancelled\n- Using a disconnected context to run cleanup activities after cancellation\n- Heartbeating in long-running activities to detect cancellation\n\n#### Start the workflow\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --workflow_type cadence_samples.CancelWorkflow \\\n  --tl cadence-samples-worker \\\n  --et 600 \\\n  --input '{}'\n```\n\nCopy the workflow ID from the output.\n\n#### Cancel the workflow\n\n```bash\ncadence --domain cadence-samples \\\n  workflow cancel \\\n  --workflow_id <YOUR_WORKFLOW_ID>\n```\n\n#### What to observe\n\nAfter cancellation:\n1. The `ActivityToBeCanceled` will detect the cancellation via `ctx.Done()` and return\n2. The `ActivityToBeSkipped` will not be scheduled (context already cancelled)\n3. The `CleanupActivity` will run using a disconnected context to perform cleanup\n\nThis pattern is essential for workflows that need to release resources or perform cleanup operations when cancelled.\n"
  },
  {
    "path": "new_samples/operations/generator/generate.go",
    "content": "package main\n\nimport \"github.com/uber-common/cadence-samples/new_samples/template\"\n\nfunc main() {\n\t// Define the data for Operations samples\n\tdata := template.TemplateData{\n\t\tSampleName: \"Operations\",\n\t\tWorkflows:  []string{\"CancelWorkflow\"},\n\t\tActivities: []string{\"ActivityToBeCanceled\", \"CleanupActivity\", \"ActivityToBeSkipped\"},\n\t}\n\n\ttemplate.GenerateAll(data)\n}\n\n// Implement custom generator below\n"
  },
  {
    "path": "new_samples/operations/main.go",
    "content": "// THIS IS A GENERATED FILE\n// PLEASE DO NOT EDIT\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc main() {\n\tStartWorker()\n\n\tdone := make(chan os.Signal, 1)\n\tsignal.Notify(done, syscall.SIGINT)\n\tfmt.Println(\"Cadence worker started, press ctrl+c to terminate...\")\n\t<-done\n}\n"
  },
  {
    "path": "new_samples/operations/worker.go",
    "content": "// THIS IS A GENERATED FILE\n// PLEASE DO NOT EDIT\n\n// Package worker implements a Cadence worker with basic configurations.\npackage main\n\nimport (\n\t\"github.com/uber-go/tally\"\n\tapiv1 \"github.com/uber/cadence-idl/go/proto/api/v1\"\n\t\"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/compatibility\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/yarpc\"\n\t\"go.uber.org/yarpc/peer\"\n\tyarpchostport \"go.uber.org/yarpc/peer/hostport\"\n\t\"go.uber.org/yarpc/transport/grpc\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nconst (\n\tHostPort = \"127.0.0.1:7833\"\n\tDomain   = \"cadence-samples\"\n\t// TaskListName identifies set of client workflows, activities, and workers.\n\t// It could be your group or client or application name.\n\tTaskListName   = \"cadence-samples-worker\"\n\tClientName     = \"cadence-samples-worker\"\n\tCadenceService = \"cadence-frontend\"\n)\n\n// StartWorker creates and starts a basic Cadence worker.\nfunc StartWorker() {\n\tlogger, cadenceClient := BuildLogger(), BuildCadenceClient()\n\tworkerOptions := worker.Options{\n\t\tLogger:       logger,\n\t\tMetricsScope: tally.NewTestScope(TaskListName, nil),\n\t}\n\n\tw := worker.New(\n\t\tcadenceClient,\n\t\tDomain,\n\t\tTaskListName,\n\t\tworkerOptions)\n\t// Workflow registration\n\tw.RegisterWorkflowWithOptions(CancelWorkflow, workflow.RegisterOptions{Name: \"cadence_samples.CancelWorkflow\"})\n\tw.RegisterActivityWithOptions(ActivityToBeCanceled, activity.RegisterOptions{Name: \"cadence_samples.ActivityToBeCanceled\"})\n\tw.RegisterActivityWithOptions(CleanupActivity, activity.RegisterOptions{Name: \"cadence_samples.CleanupActivity\"})\n\tw.RegisterActivityWithOptions(ActivityToBeSkipped, activity.RegisterOptions{Name: \"cadence_samples.ActivityToBeSkipped\"})\n\n\terr := w.Start()\n\tif err != nil {\n\t\tpanic(\"Failed to start worker: \" + err.Error())\n\t}\n\tlogger.Info(\"Started Worker.\", zap.String(\"worker\", TaskListName))\n\n}\n\nfunc BuildCadenceClient(dialOptions ...grpc.DialOption) workflowserviceclient.Interface {\n\tgrpcTransport := grpc.NewTransport()\n\t// Create a single peer chooser that identifies the host/port and configures\n\t// a gRPC dialer with TLS credentials\n\tmyChooser := peer.NewSingle(\n\t\tyarpchostport.Identify(HostPort),\n\t\tgrpcTransport.NewDialer(dialOptions...),\n\t)\n\toutbound := grpcTransport.NewOutbound(myChooser)\n\n\tdispatcher := yarpc.NewDispatcher(yarpc.Config{\n\t\tName: ClientName,\n\t\tOutbounds: yarpc.Outbounds{\n\t\t\tCadenceService: {Unary: outbound},\n\t\t},\n\t})\n\tif err := dispatcher.Start(); err != nil {\n\t\tpanic(\"Failed to start dispatcher: \" + err.Error())\n\t}\n\n\tclientConfig := dispatcher.ClientConfig(CadenceService)\n\n\t// Create a compatibility adapter that wraps proto-based YARPC clients\n\t// to provide a unified interface for domain, workflow, worker, and visibility APIs\n\treturn compatibility.NewThrift2ProtoAdapter(\n\t\tapiv1.NewDomainAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkflowAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkerAPIYARPCClient(clientConfig),\n\t\tapiv1.NewVisibilityAPIYARPCClient(clientConfig),\n\t)\n}\n\nfunc BuildLogger() *zap.Logger {\n\tconfig := zap.NewDevelopmentConfig()\n\tconfig.Level.SetLevel(zapcore.InfoLevel)\n\n\tvar err error\n\tlogger, err := config.Build()\n\tif err != nil {\n\t\tpanic(\"Failed to setup logger: \" + err.Error())\n\t}\n\n\treturn logger\n}\n"
  },
  {
    "path": "new_samples/query/README.md",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# Query Sample\n\n## Prerequisites\n\n0. Install Cadence CLI. See instruction [here](https://cadenceworkflow.io/docs/cli/).\n1. Run the Cadence server:\n    1. Clone the [Cadence](https://github.com/cadence-workflow/cadence) repository if you haven't done already: `git clone https://github.com/cadence-workflow/cadence.git`\n    2. Run `docker compose -f docker/docker-compose.yml up` to start Cadence server\n    3. See more details at https://github.com/uber/cadence/blob/master/README.md\n2. Once everything is up and running in Docker, open [localhost:8088](localhost:8088) to view Cadence UI.\n3. Register the `cadence-samples` domain:\n\n```bash\ncadence --domain cadence-samples domain register\n```\n\nRefresh the [domains page](http://localhost:8088/domains) from step 2 to verify `cadence-samples` is registered.\n\n## Steps to run sample\n\nInside the folder this sample is defined, run the following command:\n\n```bash\ngo run .\n```\n\nThis will call the main function in main.go which starts the worker, which will be execute the sample workflow code\n\n## Query Samples\n\nThis folder contains samples demonstrating how to use Cadence queries with **MarkDoc-formatted responses**. MarkDoc allows you to create interactive query responses with buttons that can signal workflows or start new workflows.\n\n### Why This Matters for Ops Teams\n\nMany teams build custom admin panels (using Retool, React, etc.) to manage long-running workflows because:\n- The CLI requires manually formatting JSON payloads\n- The generic Web UI doesn't provide context-specific actions\n- Support staff need simple buttons, not JSON knowledge\n\n**MarkDoc solves this.** Your workflow query becomes your admin panel:\n- State-appropriate buttons that change based on workflow status\n- Structured payloads sent with a single click\n- Built-in audit trail in workflow history\n- Zero additional infrastructure required\n\n---\n\n### Markdown Query Workflow\n\nA basic example demonstrating MarkDoc query usage with signal buttons.\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --tl cadence-samples-worker \\\n  --et 1000 \\\n  --workflow_type cadence_samples.MarkdownQueryWorkflow\n```\n\n#### How to interact\n\n1. Go to the `cadence-samples` domain in cadence-web and click on this workflow\n2. Click the **\"Query\"** tab\n3. Select **\"Signal\"** from the query dropdown\n4. Use the rendered buttons to control the workflow\n\n---\n\n### Lunch Vote Workflow\n\nAn interactive voting system demonstrating dynamic query responses.\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --tl cadence-samples-worker \\\n  --et 600 \\\n  --workflow_type cadence_samples.LunchVoteWorkflow\n```\n\n#### How to vote\n\n1. Navigate to the workflow in cadence-web\n2. Click the **\"Query\"** tab, select **\"options\"**\n3. Click any vote button\n4. Refresh the query to see updated vote counts\n\n---\n\n### Order Fulfillment Workflow (Admin Panel Demo)\n\n**This is the flagship sample.** It demonstrates how MarkDoc can replace custom admin panels for ops teams.\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --tl cadence-samples-worker \\\n  --et 3600 \\\n  --workflow_type cadence_samples.OrderFulfillmentWorkflow\n```\n\n#### The Scenario\n\nYou're an ops team member managing e-commerce orders. Instead of building a Retool dashboard or custom React app, you use the Cadence Web query feature as your admin panel.\n\n#### Order State Machine\n\n```\npending_payment → payment_approved → ready_to_ship → shipped → delivered\n       ↓                 ↓                 ↓\n   cancelled          refunded         cancelled\n```\n\n#### How to Use\n\n1. **Start the workflow** using the CLI command above\n2. **Open Cadence Web** at `localhost:8088`\n3. Navigate to `cadence-samples` domain → find your workflow\n4. Click the **\"Query\"** tab\n5. Select **\"dashboard\"** from the dropdown\n6. **You'll see:**\n   - Order details (customer, items, total)\n   - Current status with visual indicator\n   - State-appropriate action buttons\n   - Complete action history (audit trail)\n\n#### Walking Through the Flow\n\n**Step 1: Payment Review**\n- Status shows \"🟡 Pending Payment\"\n- Available actions: \"Approve Payment\" or \"Reject\" (with reason options)\n- Click **\"✓ Approve Payment\"**\n\n**Step 2: Fulfillment**\n- Refresh query - status now shows \"🟢 Payment Approved\"\n- Available actions: \"Mark Ready to Ship\" or \"Issue Refund\"\n- Click **\"📦 Mark Ready to Ship\"**\n\n**Step 3: Shipping**\n- Refresh query - status shows \"📦 Ready to Ship\"\n- Available actions: Ship via UPS/FedEx/USPS, or Cancel Order\n- Click **\"🚚 Ship via UPS\"**\n\n**Step 4: Delivery**\n- Refresh query - status shows \"🚚 Shipped\" with tracking number\n- Available action: \"Mark as Delivered\"\n- Click **\"✅ Mark as Delivered\"**\n\n**Step 5: Complete**\n- Status shows \"✅ Delivered\"\n- No more actions available\n- Full audit trail visible in Action History table\n\n#### Key Features Demonstrated\n\n| Feature | What It Shows |\n|---------|---------------|\n| **State-Driven UI** | Buttons change based on order status - you can't ship before payment approval |\n| **Structured Payloads** | Shipping sends `{trackingNumber, carrier}`, refunds send `{amount, reason}` |\n| **Multiple Choice via Buttons** | Rejection reasons as separate buttons - no JSON formatting needed |\n| **Audit Trail** | Every action recorded with timestamp, operator, and details |\n| **Business Context** | Order details, items, amounts displayed alongside actions |\n\n#### The Value Proposition\n\n> **\"Your workflow IS your admin panel.\"**\n\nInstead of:\n- Building a Retool dashboard\n- Maintaining a separate React app\n- Teaching ops to format JSON\n\nYou get:\n- Interactive UI generated from workflow state\n- Actions that enforce valid state transitions\n- Automatic audit logging in workflow history\n- Zero additional infrastructure\n\n---\n\n### MarkDoc Syntax Reference\n\nMarkDoc uses special tags for interactive elements:\n\n**Signal Button:**\n```\n{% signal \n    signalName=\"approve_payment\" \n    label=\"Approve\"\n    domain=\"cadence-samples\"\n    workflowId=\"your-workflow-id\"\n    runId=\"your-run-id\"\n    input={\"key\":\"value\"}\n/%}\n```\n\n**Start Workflow Button:**\n```\n{% start\n    workflowType=\"cadence_samples.MyWorkflow\" \n    label=\"Start New\"\n    domain=\"cadence-samples\"\n    taskList=\"cadence-samples-worker\"\n    workflowId=\"new-workflow-id\"\n    timeoutSeconds=60\n/%}\n```\n\n**Other Tags:**\n- `{% br /%}` - Line break\n- `{% image src=\"url\" alt=\"text\" /%}` - Image\n\n## References\n\n* The website: https://cadenceworkflow.io\n* Cadence's server: https://github.com/uber/cadence\n* Cadence's Go client: https://github.com/uber-go/cadence-client\n\n"
  },
  {
    "path": "new_samples/query/generator/README.md",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# Sample Generator\n\nThis folder is NOT part of the actual sample. It exists only for contributors who work on this sample. Please disregard it if you are trying to learn about Cadence.\n\nTo create a better learning experience for Cadence users, each sample folder is designed to be self contained. Users can view every part of writing and running workflows, including:\n\n* Cadence client initialization\n* Worker with workflow and activity registrations\n* Workflow starter\n* and the workflow code itself\n\nSome samples may have more or fewer parts depending on what they need to demonstrate.\n\nIn most cases, the workflow code (e.g. `workflow.go`) is the part that users care about. The rest is boilerplate needed to run that workflow. For each sample folder, the workflow code should be written by hand. The boilerplate can be generated. Keeping all parts inside one folder gives early learners more value because they can see everything together rather than jumping across directories.\n\n## Contributing\n\n* When creating a new sample, follow the steps mentioned in the README file in the main samples folder.\n* To update the sample workflow code, edit the workflow file directly.\n* To update the worker, client, or other boilerplate logic, edit the generator file. If your change applies to all samples, update the common generator file inside the `template` folder. Edit the generator file in this folder only when the change should affect this sample alone.\n* When you are done run the following command in the generator folder\n\n```bash\ngo run .\n```\n"
  },
  {
    "path": "new_samples/query/generator/README_specific.md",
    "content": "## Query Samples\n\nThis folder contains samples demonstrating how to use Cadence queries with **MarkDoc-formatted responses**. MarkDoc allows you to create interactive query responses with buttons that can signal workflows or start new workflows.\n\n### Why This Matters for Ops Teams\n\nMany teams build custom admin panels (using Retool, React, etc.) to manage long-running workflows because:\n- The CLI requires manually formatting JSON payloads\n- The generic Web UI doesn't provide context-specific actions\n- Support staff need simple buttons, not JSON knowledge\n\n**MarkDoc solves this.** Your workflow query becomes your admin panel:\n- State-appropriate buttons that change based on workflow status\n- Structured payloads sent with a single click\n- Built-in audit trail in workflow history\n- Zero additional infrastructure required\n\n---\n\n### Markdown Query Workflow\n\nA basic example demonstrating MarkDoc query usage with signal buttons.\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --tl cadence-samples-worker \\\n  --et 1000 \\\n  --workflow_type cadence_samples.MarkdownQueryWorkflow\n```\n\n#### How to interact\n\n1. Go to the `cadence-samples` domain in cadence-web and click on this workflow\n2. Click the **\"Query\"** tab\n3. Select **\"Signal\"** from the query dropdown\n4. Use the rendered buttons to control the workflow\n\n---\n\n### Lunch Vote Workflow\n\nAn interactive voting system demonstrating dynamic query responses.\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --tl cadence-samples-worker \\\n  --et 600 \\\n  --workflow_type cadence_samples.LunchVoteWorkflow\n```\n\n#### How to vote\n\n1. Navigate to the workflow in cadence-web\n2. Click the **\"Query\"** tab, select **\"options\"**\n3. Click any vote button\n4. Refresh the query to see updated vote counts\n\n---\n\n### Order Fulfillment Workflow (Admin Panel Demo)\n\n**This is the flagship sample.** It demonstrates how MarkDoc can replace custom admin panels for ops teams.\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --tl cadence-samples-worker \\\n  --et 3600 \\\n  --workflow_type cadence_samples.OrderFulfillmentWorkflow\n```\n\n#### The Scenario\n\nYou're an ops team member managing e-commerce orders. Instead of building a Retool dashboard or custom React app, you use the Cadence Web query feature as your admin panel.\n\n#### Order State Machine\n\n```\npending_payment → payment_approved → ready_to_ship → shipped → delivered\n       ↓                 ↓                 ↓\n   cancelled          refunded         cancelled\n```\n\n#### How to Use\n\n1. **Start the workflow** using the CLI command above\n2. **Open Cadence Web** at `localhost:8088`\n3. Navigate to `cadence-samples` domain → find your workflow\n4. Click the **\"Query\"** tab\n5. Select **\"dashboard\"** from the dropdown\n6. **You'll see:**\n   - Order details (customer, items, total)\n   - Current status with visual indicator\n   - State-appropriate action buttons\n   - Complete action history (audit trail)\n\n#### Walking Through the Flow\n\n**Step 1: Payment Review**\n- Status shows \"🟡 Pending Payment\"\n- Available actions: \"Approve Payment\" or \"Reject\" (with reason options)\n- Click **\"✓ Approve Payment\"**\n\n**Step 2: Fulfillment**\n- Refresh query - status now shows \"🟢 Payment Approved\"\n- Available actions: \"Mark Ready to Ship\" or \"Issue Refund\"\n- Click **\"📦 Mark Ready to Ship\"**\n\n**Step 3: Shipping**\n- Refresh query - status shows \"📦 Ready to Ship\"\n- Available actions: Ship via UPS/FedEx/USPS, or Cancel Order\n- Click **\"🚚 Ship via UPS\"**\n\n**Step 4: Delivery**\n- Refresh query - status shows \"🚚 Shipped\" with tracking number\n- Available action: \"Mark as Delivered\"\n- Click **\"✅ Mark as Delivered\"**\n\n**Step 5: Complete**\n- Status shows \"✅ Delivered\"\n- No more actions available\n- Full audit trail visible in Action History table\n\n#### Key Features Demonstrated\n\n| Feature | What It Shows |\n|---------|---------------|\n| **State-Driven UI** | Buttons change based on order status - you can't ship before payment approval |\n| **Structured Payloads** | Shipping sends `{trackingNumber, carrier}`, refunds send `{amount, reason}` |\n| **Multiple Choice via Buttons** | Rejection reasons as separate buttons - no JSON formatting needed |\n| **Audit Trail** | Every action recorded with timestamp, operator, and details |\n| **Business Context** | Order details, items, amounts displayed alongside actions |\n\n#### The Value Proposition\n\n> **\"Your workflow IS your admin panel.\"**\n\nInstead of:\n- Building a Retool dashboard\n- Maintaining a separate React app\n- Teaching ops to format JSON\n\nYou get:\n- Interactive UI generated from workflow state\n- Actions that enforce valid state transitions\n- Automatic audit logging in workflow history\n- Zero additional infrastructure\n\n---\n\n### MarkDoc Syntax Reference\n\nMarkDoc uses special tags for interactive elements:\n\n**Signal Button:**\n```\n{% signal \n    signalName=\"approve_payment\" \n    label=\"Approve\"\n    domain=\"cadence-samples\"\n    workflowId=\"your-workflow-id\"\n    runId=\"your-run-id\"\n    input={\"key\":\"value\"}\n/%}\n```\n\n**Start Workflow Button:**\n```\n{% start\n    workflowType=\"cadence_samples.MyWorkflow\" \n    label=\"Start New\"\n    domain=\"cadence-samples\"\n    taskList=\"cadence-samples-worker\"\n    workflowId=\"new-workflow-id\"\n    timeoutSeconds=60\n/%}\n```\n\n**Other Tags:**\n- `{% br /%}` - Line break\n- `{% image src=\"url\" alt=\"text\" /%}` - Image\n"
  },
  {
    "path": "new_samples/query/generator/generate.go",
    "content": "package main\n\nimport \"github.com/uber-common/cadence-samples/new_samples/template\"\n\nfunc main() {\n\t// Define the data for Query samples\n\tdata := template.TemplateData{\n\t\tSampleName: \"Query\",\n\t\tWorkflows:  []string{\"MarkdownQueryWorkflow\", \"LunchVoteWorkflow\", \"OrderFulfillmentWorkflow\"},\n\t\tActivities: []string{\"MarkdownQueryActivity\"},\n\t}\n\n\ttemplate.GenerateAll(data)\n}\n\n// Implement custom generator below"
  },
  {
    "path": "new_samples/query/lunch_vote_workflow.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"text/template\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n// lunchVoteFormattedResponse is the JSON shape Cadence Web expects for markdown query results (formattedData, text/markdown, data).\ntype lunchVoteFormattedResponse struct {\n\tCadenceResponseType string `json:\"cadenceResponseType\"`\n\tFormat              string `json:\"format\"`\n\tData                string `json:\"data\"`\n}\n\n// LunchVoteWorkflow demonstrates using MarkDoc query responses for interactive voting.\n// Users can vote for lunch options via signal buttons rendered in the query response.\nfunc LunchVoteWorkflow(ctx workflow.Context) error {\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"LunchVoteWorkflow started\")\n\n\tvotes := []map[string]string{}\n\n\tworkflow.SetQueryHandler(ctx, \"options\", func() (lunchVoteFormattedResponse, error) {\n\t\tlogger := workflow.GetLogger(ctx)\n\t\tlogger.Info(\"Responding to 'options' query\")\n\n\t\treturn makeLunchVoteResponse(ctx, votes), nil\n\t})\n\n\tvotesChan := workflow.GetSignalChannel(ctx, \"lunch_order\")\n\tworkflow.Go(ctx, func(ctx workflow.Context) {\n\t\tfor {\n\t\t\tvar vote map[string]string\n\t\t\tvotesChan.Receive(ctx, &vote)\n\t\t\tvotes = append(votes, vote)\n\t\t\tlogger.Info(\"Vote received\", zap.Any(\"vote\", vote))\n\t\t}\n\t})\n\n\t// Voting period - reduced from 30 minutes for sample purposes\n\terr := workflow.Sleep(ctx, 10*time.Minute)\n\tif err != nil {\n\t\tlogger.Error(\"Sleep failed\", zap.Error(err))\n\t\treturn err\n\t}\n\n\tlogger.Info(\"LunchVoteWorkflow completed.\", zap.Any(\"votes\", votes))\n\treturn nil\n}\n\n// makeLunchVoteResponse creates the MarkDoc query response for lunch voting\nfunc makeLunchVoteResponse(ctx workflow.Context, votes []map[string]string) lunchVoteFormattedResponse {\n\ttype P map[string]interface{}\n\n\tmarkdownTemplate, err := template.New(\"\").Parse(`\n## Lunch Options\n\nWe're voting on where to order lunch today. Select the option you want to vote for.\n\n---\n\n### Current Votes\n\n{{.voteTable}}\n\n### Menu Options\n\n{{.menuTable}}\n\n---\n\n### Cast Your Vote\n\n{% signal \n\tsignalName=\"lunch_order\" \n\tlabel=\"Farmhouse - Red Thai Curry\"\n\tdomain=\"cadence-samples\"\n\tcluster=\"cluster0\"\n\tworkflowId=\"{{.workflowID}}\"\n\trunId=\"{{.runID}}\"\n\tinput={\"location\":\"Farmhouse\",\"meal\":\"Red Thai Curry\",\"requests\":\"spicy\"}\n/%}\n{% signal \n\tsignalName=\"lunch_order\" \n\tlabel=\"Ethiopian Wat\"\n\tdomain=\"cadence-samples\"\n\tcluster=\"cluster0\"\n\tworkflowId=\"{{.workflowID}}\"\n\trunId=\"{{.runID}}\"\n\tinput={\"location\":\"Ethiopian\",\"meal\":\"Wat with Injera\",\"requests\":\"\"}\n/%}\n{% signal \n\tsignalName=\"lunch_order\" \n\tlabel=\"Ler Ros - Tofu Bahn Mi\"\n\tdomain=\"cadence-samples\"\n\tcluster=\"cluster0\"\n\tworkflowId=\"{{.workflowID}}\"\n\trunId=\"{{.runID}}\"\n\tinput={\"location\":\"Ler Ros\",\"meal\":\"Tofu Bahn Mi\",\"requests\":\"\"}\n/%}\n\n{% br /%}\n\n*Vote closes when workflow times out (10 minutes)*\n\t`)\n\tif err != nil {\n\t\tpanic(\"Failed to parse template: \" + err.Error())\n\t}\n\n\tvar markdown bytes.Buffer\n\terr = markdownTemplate.Execute(&markdown, P{\n\t\t\"workflowID\": workflow.GetInfo(ctx).WorkflowExecution.ID,\n\t\t\"runID\":      workflow.GetInfo(ctx).WorkflowExecution.RunID,\n\t\t\"voteTable\":  makeLunchVoteTable(votes),\n\t\t\"menuTable\":  makeLunchMenu(),\n\t})\n\tif err != nil {\n\t\tpanic(\"Failed to execute template: \" + err.Error())\n\t}\n\n\treturn lunchVoteFormattedResponse{\n\t\tCadenceResponseType: \"formattedData\",\n\t\tFormat:              \"text/markdown\",\n\t\tData:                markdown.String(),\n\t}\n}\n\n// makeLunchVoteTable generates a markdown table of current votes\nfunc makeLunchVoteTable(votes []map[string]string) string {\n\tif len(votes) == 0 {\n\t\treturn \"| Location | Meal | Requests |\\n|----------|------|----------|\\n| *No votes yet* | | |\\n\"\n\t}\n\ttable := \"| Location | Meal | Requests |\\n|----------|------|----------|\\n\"\n\tfor _, vote := range votes {\n\t\tloc := vote[\"location\"]\n\t\tmeal := vote[\"meal\"]\n\t\trequests := vote[\"requests\"]\n\t\ttable += \"| \" + loc + \" | \" + meal + \" | \" + requests + \" |\\n\"\n\t}\n\treturn table\n}\n\n// makeLunchMenu generates a markdown table with menu options and images\nfunc makeLunchMenu() string {\n\toptions := []struct {\n\t\timage string\n\t\tdesc  string\n\t}{\n\t\t{\n\t\t\timage: \"https://upload.wikimedia.org/wikipedia/commons/thumb/e/e2/Red_roast_duck_curry.jpg/200px-Red_roast_duck_curry.jpg\",\n\t\t\tdesc:  \"**Farmhouse - Red Thai Curry**: A dish in Thai cuisine made from curry paste, coconut milk, meat, seafood, vegetables, and herbs.\",\n\t\t},\n\t\t{\n\t\t\timage: \"https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/B%C3%A1nh_m%C3%AC_th%E1%BB%8Bt_n%C6%B0%E1%BB%9Bng.png/200px-B%C3%A1nh_m%C3%AC_th%E1%BB%8Bt_n%C6%B0%E1%BB%9Bng.png\",\n\t\t\tdesc:  \"**Ler Ros - Tofu Bahn Mi**: A Vietnamese sandwich with a baguette filled with lemongrass tofu, vegetables, and fresh herbs.\",\n\t\t},\n\t\t{\n\t\t\timage: \"https://upload.wikimedia.org/wikipedia/commons/thumb/5/54/Ethiopian_wat.jpg/960px-Ethiopian_wat.jpg\",\n\t\t\tdesc:  \"**Ethiopian Wat**: A traditional Ethiopian stew made from spices, vegetables, and legumes, served with injera flatbread.\",\n\t\t},\n\t}\n\n\ttable := \"| Picture | Description |\\n|---------|-------------|\\n\"\n\tfor _, option := range options {\n\t\ttable += \"| ![food](\" + option.image + \") | \" + option.desc + \" |\\n\"\n\t}\n\ttable += \"\\n*(source: wikipedia)*\"\n\n\treturn table\n}\n"
  },
  {
    "path": "new_samples/query/main.go",
    "content": "// THIS IS A GENERATED FILE\n// PLEASE DO NOT EDIT\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc main() {\n\tStartWorker()\n\n\tdone := make(chan os.Signal, 1)\n\tsignal.Notify(done, syscall.SIGINT)\n\tfmt.Println(\"Cadence worker started, press ctrl+c to terminate...\")\n\t<-done\n}\n"
  },
  {
    "path": "new_samples/query/markdown_query.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"bytes\"\n\t\"strconv\"\n\t\"text/template\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\nconst (\n\tCompleteSignalChan = \"complete\"\n)\n\n// markdownFormattedResponse is the JSON shape Cadence Web expects for markdown query results (formattedData, text/markdown, data).\ntype markdownFormattedResponse struct {\n\tCadenceResponseType string `json:\"cadenceResponseType\"`\n\tFormat              string `json:\"format\"`\n\tData                string `json:\"data\"`\n}\n\nfunc MarkdownQueryWorkflow(ctx workflow.Context) error {\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute * 60,\n\t\tStartToCloseTimeout:    time.Minute * 60,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"MarkdownQueryWorkflow started\")\n\n\tworkflow.SetQueryHandler(ctx, \"Signal\", func() (markdownFormattedResponse, error) {\n\t\tlogger := workflow.GetLogger(ctx)\n\t\tlogger.Info(\"Responding to 'Signal' query\")\n\n\t\treturn makeMarkdownQueryResponse(ctx), nil\n\t})\n\n\tvar complete bool\n\tcompleteChan := workflow.GetSignalChannel(ctx, CompleteSignalChan)\n\tfor {\n\t\ts := workflow.NewSelector(ctx)\n\t\ts.AddReceive(completeChan, func(ch workflow.Channel, ok bool) {\n\t\t\tif ok {\n\t\t\t\tch.Receive(ctx, &complete)\n\t\t\t}\n\t\t\tlogger.Info(\"Signal input: \" + strconv.FormatBool(complete))\n\t\t})\n\t\ts.Select(ctx)\n\n\t\tvar result string\n\t\terr := workflow.ExecuteActivity(ctx, MarkdownQueryActivity, complete).Get(ctx, &result)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tlogger.Info(\"Activity result: \" + result)\n\t\tif complete {\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\nfunc makeMarkdownQueryResponse(ctx workflow.Context) markdownFormattedResponse {\n\ttype P map[string]interface{}\n\n\tmarkdownTemplate, err := template.New(\"\").Parse(`\n\t## Markdown Query Workflow\n\t\n\tYou can use markdown as your query response, which also supports starting and signaling workflows.\n\t\n\t* Use the Complete button to complete this workflow.\n\t* Use the Continue button just to send a signal to continue this workflow.\n\t* Or you can use the \"Start Another\" button to start another workflow of this type.\n\t\n\t{% signal \n\t\tsignalName=\"complete\" \n\t\tlabel=\"Complete\"\n\t\tdomain=\"cadence-samples\"\n\t\tcluster=\"cluster0\"\n\t\tworkflowId=\"{{.workflowID}}\"\n\t\trunId=\"{{.runID}}\"\n\t\tinput=true\n\t/%}\n\t{% signal\n\t\tsignalName=\"complete\" \n\t\tlabel=\"Continue\"\n\t\tdomain=\"cadence-samples\"\n\t\tcluster=\"cluster0\"\n\t\tworkflowId=\"{{.workflowID}}\"\n\t\trunId=\"{{.runID}}\"\n\t\tinput=false\n\t/%}\n\t{% start\n\t\tworkflowType=\"cadence_samples.MarkdownQueryWorkflow\" \n\t\tlabel=\"Start Another\"\n\t\tdomain=\"cadence-samples\"\n\t\tcluster=\"cluster0\"\n\t\ttaskList=\"cadence-samples-worker\"\n\t\tworkflowId=\"{{.newWorkflowID}}\"\n\t\ttimeoutSeconds=60\n\t/%}\n\t\n\t{% br /%} \n\t{% image src=\"https://cadenceworkflow.io/img/cadence-logo.svg\" alt=\"Cadence Logo\" height=\"100\" /%}\n\t\t`)\n\tif err != nil {\n\t\tpanic(\"Failed to parse template: \" + err.Error())\n\t}\n\n\tvar markdown bytes.Buffer\n\terr = markdownTemplate.Execute(&markdown, P{\n\t\t\"workflowID\": workflow.GetInfo(ctx).WorkflowExecution.ID,\n\t\t\"runID\":      workflow.GetInfo(ctx).WorkflowExecution.RunID,\n\t\t\"newWorkflowID\": \"markdown-\" + strconv.FormatInt(time.Now().UnixNano()/1000000, 10),\n\t})\n\tif err != nil {\n\t\tpanic(\"Failed to execute template: \" + err.Error())\n\t}\n\n\treturn markdownFormattedResponse{\n\t\tCadenceResponseType: \"formattedData\",\n\t\tFormat:              \"text/markdown\",\n\t\tData:                markdown.String(),\n\t}\n}\n\nfunc MarkdownQueryActivity(ctx context.Context, complete bool) (string, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"MarkdownQueryActivity started, a new signal has been received\", zap.Bool(\"complete\", complete))\n\tif complete {\n\t\treturn \"Workflow will complete now\", nil\n\t}\n\treturn \"Workflow will continue to run\", nil\n}\n"
  },
  {
    "path": "new_samples/query/order_fulfillment_workflow.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"text/template\"\n\t\"time\"\n\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/zap\"\n)\n\n// orderDashboardFormattedResponse is the JSON shape Cadence Web expects for markdown query results (formattedData, text/markdown, data).\ntype orderDashboardFormattedResponse struct {\n\tCadenceResponseType string `json:\"cadenceResponseType\"`\n\tFormat              string `json:\"format\"`\n\tData                string `json:\"data\"`\n}\n\n// Order represents an e-commerce order being fulfilled\ntype Order struct {\n\tOrderID       string\n\tCustomerName  string\n\tCustomerEmail string\n\tItems         []OrderItem\n\tTotalAmount   float64\n\tStatus        string\n\tTrackingNum   string\n\tCarrier       string\n\tRefundAmount  float64\n\tRefundReason  string\n\tCreatedAt     time.Time\n}\n\n// OrderItem represents a line item in an order\ntype OrderItem struct {\n\tName     string\n\tQuantity int\n\tPrice    float64\n}\n\n// ActionLogEntry represents an ops action taken on the order\ntype ActionLogEntry struct {\n\tTimestamp time.Time\n\tAction    string\n\tOperator  string\n\tDetails   string\n}\n\n// Order status constants\nconst (\n\tStatusPendingPayment  = \"pending_payment\"\n\tStatusPaymentApproved = \"payment_approved\"\n\tStatusReadyToShip     = \"ready_to_ship\"\n\tStatusShipped         = \"shipped\"\n\tStatusDelivered       = \"delivered\"\n\tStatusCancelled       = \"cancelled\"\n\tStatusRefunded        = \"refunded\"\n)\n\n// Signal payloads\ntype RejectPaymentSignal struct {\n\tReason   string `json:\"reason\"`\n\tOperator string `json:\"operator\"`\n}\n\ntype ApprovePaymentSignal struct {\n\tOperator string `json:\"operator\"`\n}\n\ntype ShipOrderSignal struct {\n\tTrackingNumber string `json:\"trackingNumber\"`\n\tCarrier        string `json:\"carrier\"`\n\tOperator       string `json:\"operator\"`\n}\n\ntype RefundSignal struct {\n\tAmount   float64 `json:\"amount\"`\n\tReason   string  `json:\"reason\"`\n\tOperator string  `json:\"operator\"`\n}\n\ntype CancelOrderSignal struct {\n\tReason   string `json:\"reason\"`\n\tOperator string `json:\"operator\"`\n}\n\ntype SimpleSignal struct {\n\tOperator string `json:\"operator\"`\n}\n\n// OrderFulfillmentWorkflow demonstrates a state-driven MarkDoc UI for ops teams.\n// This workflow shows how Cadence Web queries can replace custom admin panels.\nfunc OrderFulfillmentWorkflow(ctx workflow.Context) error {\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"OrderFulfillmentWorkflow started\")\n\n\t// Initialize sample order\n\torder := Order{\n\t\tOrderID:       \"ORD-2024-001234\",\n\t\tCustomerName:  \"Alice Johnson\",\n\t\tCustomerEmail: \"alice.johnson@example.com\",\n\t\tItems: []OrderItem{\n\t\t\t{Name: \"Wireless Headphones\", Quantity: 2, Price: 79.99},\n\t\t\t{Name: \"Phone Case\", Quantity: 1, Price: 19.99},\n\t\t},\n\t\tTotalAmount: 179.97,\n\t\tStatus:      StatusPendingPayment,\n\t\tCreatedAt:   workflow.Now(ctx),\n\t}\n\n\t// Action log for audit trail\n\tactionLog := []ActionLogEntry{\n\t\t{\n\t\t\tTimestamp: workflow.Now(ctx),\n\t\t\tAction:    \"Order Created\",\n\t\t\tOperator:  \"System\",\n\t\t\tDetails:   fmt.Sprintf(\"Order %s created for %s\", order.OrderID, order.CustomerName),\n\t\t},\n\t}\n\n\t// Register query handler for the ops dashboard\n\tworkflow.SetQueryHandler(ctx, \"dashboard\", func() (orderDashboardFormattedResponse, error) {\n\t\tlogger.Info(\"Responding to 'dashboard' query\")\n\t\treturn makeOrderDashboard(ctx, order, actionLog), nil\n\t})\n\n\t// Set up signal channels\n\tapprovePaymentChan := workflow.GetSignalChannel(ctx, \"approve_payment\")\n\trejectPaymentChan := workflow.GetSignalChannel(ctx, \"reject_payment\")\n\tmarkReadyChan := workflow.GetSignalChannel(ctx, \"mark_ready_to_ship\")\n\tshipOrderChan := workflow.GetSignalChannel(ctx, \"ship_order\")\n\trefundChan := workflow.GetSignalChannel(ctx, \"issue_refund\")\n\tcancelChan := workflow.GetSignalChannel(ctx, \"cancel_order\")\n\tdeliveredChan := workflow.GetSignalChannel(ctx, \"mark_delivered\")\n\n\t// Main workflow loop - process signals until terminal state\n\tfor !isTerminalState(order.Status) {\n\t\tselector := workflow.NewSelector(ctx)\n\n\t\tselector.AddReceive(approvePaymentChan, func(ch workflow.Channel, ok bool) {\n\t\t\tvar signal ApprovePaymentSignal\n\t\t\tch.Receive(ctx, &signal)\n\t\t\tif order.Status == StatusPendingPayment {\n\t\t\t\torder.Status = StatusPaymentApproved\n\t\t\t\tactionLog = append(actionLog, ActionLogEntry{\n\t\t\t\t\tTimestamp: workflow.Now(ctx),\n\t\t\t\t\tAction:    \"Payment Approved\",\n\t\t\t\t\tOperator:  getOperator(signal.Operator),\n\t\t\t\t\tDetails:   fmt.Sprintf(\"Payment of $%.2f approved\", order.TotalAmount),\n\t\t\t\t})\n\t\t\t\tlogger.Info(\"Payment approved\", zap.String(\"operator\", signal.Operator))\n\t\t\t}\n\t\t})\n\n\t\tselector.AddReceive(rejectPaymentChan, func(ch workflow.Channel, ok bool) {\n\t\t\tvar signal RejectPaymentSignal\n\t\t\tch.Receive(ctx, &signal)\n\t\t\tif order.Status == StatusPendingPayment {\n\t\t\t\torder.Status = StatusCancelled\n\t\t\t\tactionLog = append(actionLog, ActionLogEntry{\n\t\t\t\t\tTimestamp: workflow.Now(ctx),\n\t\t\t\t\tAction:    \"Payment Rejected\",\n\t\t\t\t\tOperator:  getOperator(signal.Operator),\n\t\t\t\t\tDetails:   fmt.Sprintf(\"Reason: %s\", signal.Reason),\n\t\t\t\t})\n\t\t\t\tlogger.Info(\"Payment rejected\", zap.String(\"reason\", signal.Reason))\n\t\t\t}\n\t\t})\n\n\t\tselector.AddReceive(markReadyChan, func(ch workflow.Channel, ok bool) {\n\t\t\tvar signal SimpleSignal\n\t\t\tch.Receive(ctx, &signal)\n\t\t\tif order.Status == StatusPaymentApproved {\n\t\t\t\torder.Status = StatusReadyToShip\n\t\t\t\tactionLog = append(actionLog, ActionLogEntry{\n\t\t\t\t\tTimestamp: workflow.Now(ctx),\n\t\t\t\t\tAction:    \"Marked Ready to Ship\",\n\t\t\t\t\tOperator:  getOperator(signal.Operator),\n\t\t\t\t\tDetails:   \"Order prepared and ready for shipping\",\n\t\t\t\t})\n\t\t\t\tlogger.Info(\"Order marked ready to ship\")\n\t\t\t}\n\t\t})\n\n\t\tselector.AddReceive(shipOrderChan, func(ch workflow.Channel, ok bool) {\n\t\t\tvar signal ShipOrderSignal\n\t\t\tch.Receive(ctx, &signal)\n\t\t\tif order.Status == StatusReadyToShip {\n\t\t\t\torder.Status = StatusShipped\n\t\t\t\torder.TrackingNum = signal.TrackingNumber\n\t\t\t\torder.Carrier = signal.Carrier\n\t\t\t\tactionLog = append(actionLog, ActionLogEntry{\n\t\t\t\t\tTimestamp: workflow.Now(ctx),\n\t\t\t\t\tAction:    \"Order Shipped\",\n\t\t\t\t\tOperator:  getOperator(signal.Operator),\n\t\t\t\t\tDetails:   fmt.Sprintf(\"Carrier: %s, Tracking: %s\", signal.Carrier, signal.TrackingNumber),\n\t\t\t\t})\n\t\t\t\tlogger.Info(\"Order shipped\", zap.String(\"tracking\", signal.TrackingNumber))\n\t\t\t}\n\t\t})\n\n\t\tselector.AddReceive(refundChan, func(ch workflow.Channel, ok bool) {\n\t\t\tvar signal RefundSignal\n\t\t\tch.Receive(ctx, &signal)\n\t\t\tif order.Status == StatusPaymentApproved {\n\t\t\t\torder.Status = StatusRefunded\n\t\t\t\torder.RefundAmount = signal.Amount\n\t\t\t\torder.RefundReason = signal.Reason\n\t\t\t\tactionLog = append(actionLog, ActionLogEntry{\n\t\t\t\t\tTimestamp: workflow.Now(ctx),\n\t\t\t\t\tAction:    \"Refund Issued\",\n\t\t\t\t\tOperator:  getOperator(signal.Operator),\n\t\t\t\t\tDetails:   fmt.Sprintf(\"Amount: $%.2f, Reason: %s\", signal.Amount, signal.Reason),\n\t\t\t\t})\n\t\t\t\tlogger.Info(\"Refund issued\", zap.Float64(\"amount\", signal.Amount))\n\t\t\t}\n\t\t})\n\n\t\tselector.AddReceive(cancelChan, func(ch workflow.Channel, ok bool) {\n\t\t\tvar signal CancelOrderSignal\n\t\t\tch.Receive(ctx, &signal)\n\t\t\tif order.Status == StatusReadyToShip {\n\t\t\t\torder.Status = StatusCancelled\n\t\t\t\tactionLog = append(actionLog, ActionLogEntry{\n\t\t\t\t\tTimestamp: workflow.Now(ctx),\n\t\t\t\t\tAction:    \"Order Cancelled\",\n\t\t\t\t\tOperator:  getOperator(signal.Operator),\n\t\t\t\t\tDetails:   fmt.Sprintf(\"Reason: %s\", signal.Reason),\n\t\t\t\t})\n\t\t\t\tlogger.Info(\"Order cancelled\", zap.String(\"reason\", signal.Reason))\n\t\t\t}\n\t\t})\n\n\t\tselector.AddReceive(deliveredChan, func(ch workflow.Channel, ok bool) {\n\t\t\tvar signal SimpleSignal\n\t\t\tch.Receive(ctx, &signal)\n\t\t\tif order.Status == StatusShipped {\n\t\t\t\torder.Status = StatusDelivered\n\t\t\t\tactionLog = append(actionLog, ActionLogEntry{\n\t\t\t\t\tTimestamp: workflow.Now(ctx),\n\t\t\t\t\tAction:    \"Order Delivered\",\n\t\t\t\t\tOperator:  getOperator(signal.Operator),\n\t\t\t\t\tDetails:   \"Package confirmed delivered to customer\",\n\t\t\t\t})\n\t\t\t\tlogger.Info(\"Order marked as delivered\")\n\t\t\t}\n\t\t})\n\n\t\tselector.Select(ctx)\n\t}\n\n\tlogger.Info(\"OrderFulfillmentWorkflow completed\", zap.String(\"finalStatus\", order.Status))\n\treturn nil\n}\n\nfunc isTerminalState(status string) bool {\n\treturn status == StatusDelivered || status == StatusCancelled || status == StatusRefunded\n}\n\nfunc getOperator(operator string) string {\n\tif operator == \"\" {\n\t\treturn \"ops-user\"\n\t}\n\treturn operator\n}\n\nfunc makeOrderDashboard(ctx workflow.Context, order Order, actionLog []ActionLogEntry) orderDashboardFormattedResponse {\n\ttype P map[string]interface{}\n\n\tmarkdownTemplate, err := template.New(\"\").Parse(`\n## 🛒 Order Dashboard\n\n> **Your admin panel** - manage orders directly from Cadence Web.\n\n---\n\n### ⚡ Available Actions\n{{.actionButtons}}\n\n---\n\n### 📋 Order Details\n\n| Field | Value |\n|-------|-------|\n| **Order ID** | {{.orderID}} |\n| **Customer** | {{.customerName}} |\n| **Email** | {{.customerEmail}} |\n| **Created** | {{.createdAt}} |\n| **Status** | {{.statusBadge}} |\n{{if .trackingNum}} **Tracking: {{.carrier}} - {{.trackingNum}}** {{end}}\n{{if .refundAmount}}**Refund: ${{.refundAmount}}** {{end}}\n\n### 📦 Order Items\n\n| Item | Qty | Price | Subtotal |\n|------|-----|-------|----------|\n{{.itemsTable}}\n\n**Total: ${{.totalAmount}}**\n\n---\n\n### ⏱️ Action History\n\n| Timestamp | Action | Operator | Details |\n|-----------|--------|----------|---------|\n{{.actionHistory}}\n\n---\n\n*Click query \"Run\" button again to see updated status after taking an action.*\n\t`)\n\tif err != nil {\n\t\tpanic(\"Failed to parse template: \" + err.Error())\n\t}\n\n\tvar markdown bytes.Buffer\n\terr = markdownTemplate.Execute(&markdown, P{\n\t\t\"orderID\":       order.OrderID,\n\t\t\"customerName\":  order.CustomerName,\n\t\t\"customerEmail\": order.CustomerEmail,\n\t\t\"createdAt\":     order.CreatedAt.Format(\"2006-01-02 15:04:05\"),\n\t\t\"statusBadge\":   getStatusBadge(order.Status),\n\t\t\"trackingNum\":   order.TrackingNum,\n\t\t\"carrier\":       order.Carrier,\n\t\t\"refundAmount\":  fmt.Sprintf(\"%.2f\", order.RefundAmount),\n\t\t\"refundReason\":  order.RefundReason,\n\t\t\"totalAmount\":   fmt.Sprintf(\"%.2f\", order.TotalAmount),\n\t\t\"itemsTable\":    makeItemsTable(order.Items),\n\t\t\"actionButtons\": makeActionButtons(ctx, order),\n\t\t\"actionHistory\": makeActionHistory(actionLog),\n\t})\n\tif err != nil {\n\t\tpanic(\"Failed to execute template: \" + err.Error())\n\t}\n\n\treturn orderDashboardFormattedResponse{\n\t\tCadenceResponseType: \"formattedData\",\n\t\tFormat:              \"text/markdown\",\n\t\tData:                markdown.String(),\n\t}\n}\n\nfunc getStatusBadge(status string) string {\n\tbadges := map[string]string{\n\t\tStatusPendingPayment:  \"🟡 **Pending Payment**\",\n\t\tStatusPaymentApproved: \"🟢 **Payment Approved**\",\n\t\tStatusReadyToShip:     \"📦 **Ready to Ship**\",\n\t\tStatusShipped:         \"🚚 **Shipped**\",\n\t\tStatusDelivered:       \"✅ **Delivered**\",\n\t\tStatusCancelled:       \"❌ **Cancelled**\",\n\t\tStatusRefunded:        \"💰 **Refunded**\",\n\t}\n\tif badge, ok := badges[status]; ok {\n\t\treturn badge\n\t}\n\treturn status\n}\n\nfunc makeItemsTable(items []OrderItem) string {\n\ttable := \"\"\n\tfor _, item := range items {\n\t\tsubtotal := float64(item.Quantity) * item.Price\n\t\ttable += fmt.Sprintf(\"| %s | %d | $%.2f | $%.2f |\\n\", item.Name, item.Quantity, item.Price, subtotal)\n\t}\n\treturn table\n}\n\nfunc makeActionButtons(ctx workflow.Context, order Order) string {\n\tworkflowID := workflow.GetInfo(ctx).WorkflowExecution.ID\n\trunID := workflow.GetInfo(ctx).WorkflowExecution.RunID\n\n\tvar buttons string\n\n\tswitch order.Status {\n\tcase StatusPendingPayment:\n\t\tbuttons = fmt.Sprintf(`\n**Payment Review:**\n\n{%% signal \n\tsignalName=\"approve_payment\" \n\tlabel=\"✓ Approve Payment\"\n\tdomain=\"cadence-samples\"\n\tcluster=\"cluster0\"\n\tworkflowId=\"%s\"\n\trunId=\"%s\"\n\tinput={\"operator\":\"ops-user\"}\n/%%}\n{%% signal \n\tsignalName=\"reject_payment\" \n\tlabel=\"✗ Reject: Policy Violation\"\n\tdomain=\"cadence-samples\"\n\tcluster=\"cluster0\"\n\tworkflowId=\"%s\"\n\trunId=\"%s\"\n\tinput={\"reason\":\"Policy Violation\",\"operator\":\"ops-user\"}\n/%%}\n{%% signal \n\tsignalName=\"reject_payment\" \n\tlabel=\"✗ Reject: Fraud Suspected\"\n\tdomain=\"cadence-samples\"\n\tcluster=\"cluster0\"\n\tworkflowId=\"%s\"\n\trunId=\"%s\"\n\tinput={\"reason\":\"Fraud Suspected\",\"operator\":\"ops-user\"}\n/%%}\n{%% signal \n\tsignalName=\"reject_payment\" \n\tlabel=\"✗ Reject: Customer Request\"\n\tdomain=\"cadence-samples\"\n\tcluster=\"cluster0\"\n\tworkflowId=\"%s\"\n\trunId=\"%s\"\n\tinput={\"reason\":\"Customer Request\",\"operator\":\"ops-user\"}\n/%%}\n`, workflowID, runID, workflowID, runID, workflowID, runID, workflowID, runID)\n\n\tcase StatusPaymentApproved:\n\t\tbuttons = fmt.Sprintf(`\n**Fulfillment Actions:**\n\n{%% signal \n\tsignalName=\"mark_ready_to_ship\" \n\tlabel=\"📦 Mark Ready to Ship\"\n\tdomain=\"cadence-samples\"\n\tcluster=\"cluster0\"\n\tworkflowId=\"%s\"\n\trunId=\"%s\"\n\tinput={\"operator\":\"ops-user\"}\n/%%}\n\n**Refund Options:**\n\n{%% signal \n\tsignalName=\"issue_refund\" \n\tlabel=\"💰 Full Refund ($%.2f)\"\n\tdomain=\"cadence-samples\"\n\tcluster=\"cluster0\"\n\tworkflowId=\"%s\"\n\trunId=\"%s\"\n\tinput={\"amount\":%.2f,\"reason\":\"Full refund requested\",\"operator\":\"ops-user\"}\n/%%}\n{%% signal \n\tsignalName=\"issue_refund\" \n\tlabel=\"💰 Partial Refund (50%%)\"\n\tdomain=\"cadence-samples\"\n\tcluster=\"cluster0\"\n\tworkflowId=\"%s\"\n\trunId=\"%s\"\n\tinput={\"amount\":%.2f,\"reason\":\"Partial refund - customer goodwill\",\"operator\":\"ops-user\"}\n/%%}\n`, workflowID, runID, order.TotalAmount, workflowID, runID, order.TotalAmount, workflowID, runID, order.TotalAmount/2)\n\n\tcase StatusReadyToShip:\n\t\tbuttons = fmt.Sprintf(`\n**Shipping Options:**\n\n{%% signal \n\tsignalName=\"ship_order\" \n\tlabel=\"🚚 Ship via UPS\"\n\tdomain=\"cadence-samples\"\n\tcluster=\"cluster0\"\n\tworkflowId=\"%s\"\n\trunId=\"%s\"\n\tinput={\"trackingNumber\":\"1Z999AA10123456784\",\"carrier\":\"UPS\",\"operator\":\"ops-user\"}\n/%%}\n{%% signal \n\tsignalName=\"ship_order\" \n\tlabel=\"🚚 Ship via FedEx\"\n\tdomain=\"cadence-samples\"\n\tcluster=\"cluster0\"\n\tworkflowId=\"%s\"\n\trunId=\"%s\"\n\tinput={\"trackingNumber\":\"794644790126\",\"carrier\":\"FedEx\",\"operator\":\"ops-user\"}\n/%%}\n{%% signal \n\tsignalName=\"ship_order\" \n\tlabel=\"🚚 Ship via USPS\"\n\tdomain=\"cadence-samples\"\n\tcluster=\"cluster0\"\n\tworkflowId=\"%s\"\n\trunId=\"%s\"\n\tinput={\"trackingNumber\":\"9400111899223456789012\",\"carrier\":\"USPS\",\"operator\":\"ops-user\"}\n/%%}\n\n**Cancel Order:**\n\n{%% signal \n\tsignalName=\"cancel_order\" \n\tlabel=\"❌ Cancel Order\"\n\tdomain=\"cadence-samples\"\n\tcluster=\"cluster0\"\n\tworkflowId=\"%s\"\n\trunId=\"%s\"\n\tinput={\"reason\":\"Cancelled before shipping\",\"operator\":\"ops-user\"}\n/%%}\n`, workflowID, runID, workflowID, runID, workflowID, runID, workflowID, runID)\n\n\tcase StatusShipped:\n\t\tbuttons = fmt.Sprintf(`\n**Delivery Confirmation:**\n\n{%% signal \n\tsignalName=\"mark_delivered\" \n\tlabel=\"✅ Mark as Delivered\"\n\tdomain=\"cadence-samples\"\n\tcluster=\"cluster0\"\n\tworkflowId=\"%s\"\n\trunId=\"%s\"\n\tinput={\"operator\":\"ops-user\"}\n/%%}\n`, workflowID, runID)\n\n\tdefault:\n\t\tbuttons = `\n*No actions available - order has been completed.*\n`\n\t}\n\n\treturn buttons\n}\n\nfunc makeActionHistory(actionLog []ActionLogEntry) string {\n\thistory := \"\"\n\tfor _, entry := range actionLog {\n\t\thistory += fmt.Sprintf(\"| %s | %s | %s | %s |\\n\",\n\t\t\tentry.Timestamp.Format(\"15:04:05\"),\n\t\t\tentry.Action,\n\t\t\tentry.Operator,\n\t\t\tentry.Details)\n\t}\n\treturn history\n}\n"
  },
  {
    "path": "new_samples/query/worker.go",
    "content": "// THIS IS A GENERATED FILE\n// PLEASE DO NOT EDIT\n\n// Package worker implements a Cadence worker with basic configurations.\npackage main\n\nimport (\n\t\"github.com/uber-go/tally\"\n\tapiv1 \"github.com/uber/cadence-idl/go/proto/api/v1\"\n\t\"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/compatibility\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/yarpc\"\n\t\"go.uber.org/yarpc/peer\"\n\tyarpchostport \"go.uber.org/yarpc/peer/hostport\"\n\t\"go.uber.org/yarpc/transport/grpc\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nconst (\n\tHostPort = \"127.0.0.1:7833\"\n\tDomain   = \"cadence-samples\"\n\t// TaskListName identifies set of client workflows, activities, and workers.\n\t// It could be your group or client or application name.\n\tTaskListName   = \"cadence-samples-worker\"\n\tClientName     = \"cadence-samples-worker\"\n\tCadenceService = \"cadence-frontend\"\n)\n\n// StartWorker creates and starts a basic Cadence worker.\nfunc StartWorker() {\n\tlogger, cadenceClient := BuildLogger(), BuildCadenceClient()\n\tworkerOptions := worker.Options{\n\t\tLogger:       logger,\n\t\tMetricsScope: tally.NewTestScope(TaskListName, nil),\n\t}\n\n\tw := worker.New(\n\t\tcadenceClient,\n\t\tDomain,\n\t\tTaskListName,\n\t\tworkerOptions)\n\t// workflow registration\n\tw.RegisterWorkflowWithOptions(MarkdownQueryWorkflow, workflow.RegisterOptions{Name: \"cadence_samples.MarkdownQueryWorkflow\"})\n\tw.RegisterWorkflowWithOptions(LunchVoteWorkflow, workflow.RegisterOptions{Name: \"cadence_samples.LunchVoteWorkflow\"})\n\tw.RegisterWorkflowWithOptions(OrderFulfillmentWorkflow, workflow.RegisterOptions{Name: \"cadence_samples.OrderFulfillmentWorkflow\"})\n\tw.RegisterActivityWithOptions(MarkdownQueryActivity, activity.RegisterOptions{Name: \"cadence_samples.MarkdownQueryActivity\"})\n\n\terr := w.Start()\n\tif err != nil {\n\t\tpanic(\"Failed to start worker: \" + err.Error())\n\t}\n\tlogger.Info(\"Started Worker.\", zap.String(\"worker\", TaskListName))\n\n}\n\nfunc BuildCadenceClient(dialOptions ...grpc.DialOption) workflowserviceclient.Interface {\n\tgrpcTransport := grpc.NewTransport()\n\t// Create a single peer chooser that identifies the host/port and configures\n\t// a gRPC dialer with TLS credentials\n\tmyChooser := peer.NewSingle(\n\t\tyarpchostport.Identify(HostPort),\n\t\tgrpcTransport.NewDialer(dialOptions...),\n\t)\n\toutbound := grpcTransport.NewOutbound(myChooser)\n\n\tdispatcher := yarpc.NewDispatcher(yarpc.Config{\n\t\tName: ClientName,\n\t\tOutbounds: yarpc.Outbounds{\n\t\t\tCadenceService: {Unary: outbound},\n\t\t},\n\t})\n\tif err := dispatcher.Start(); err != nil {\n\t\tpanic(\"Failed to start dispatcher: \" + err.Error())\n\t}\n\n\tclientConfig := dispatcher.ClientConfig(CadenceService)\n\n\t// Create a compatibility adapter that wraps proto-based YARPC clients\n\t// to provide a unified interface for domain, workflow, worker, and visibility APIs\n\treturn compatibility.NewThrift2ProtoAdapter(\n\t\tapiv1.NewDomainAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkflowAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkerAPIYARPCClient(clientConfig),\n\t\tapiv1.NewVisibilityAPIYARPCClient(clientConfig),\n\t)\n}\n\nfunc BuildLogger() *zap.Logger {\n\tconfig := zap.NewDevelopmentConfig()\n\tconfig.Level.SetLevel(zapcore.InfoLevel)\n\n\tvar err error\n\tlogger, err := config.Build()\n\tif err != nil {\n\t\tpanic(\"Failed to setup logger: \" + err.Error())\n\t}\n\n\treturn logger\n}\n"
  },
  {
    "path": "new_samples/signal/README.md",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# Signal Workflow Sample\n\n## Prerequisites\n\n0. Install Cadence CLI. See instruction [here](https://cadenceworkflow.io/docs/cli/).\n1. Run the Cadence server:\n    1. Clone the [Cadence](https://github.com/cadence-workflow/cadence) repository if you haven't done already: `git clone https://github.com/cadence-workflow/cadence.git`\n    2. Run `docker compose -f docker/docker-compose.yml up` to start Cadence server\n    3. See more details at https://github.com/uber/cadence/blob/master/README.md\n2. Once everything is up and running in Docker, open [localhost:8088](localhost:8088) to view Cadence UI.\n3. Register the `cadence-samples` domain:\n\n```bash\ncadence --domain cadence-samples domain register\n```\n\nRefresh the [domains page](http://localhost:8088/domains) from step 2 to verify `cadence-samples` is registered.\n\n## Steps to run sample\n\nInside the folder this sample is defined, run the following command:\n\n```bash\ngo run .\n```\n\nThis will call the main function in main.go which starts the worker, which will be execute the sample workflow code\n\n## Simple Signal Workflow\n\nThis workflow takes an input message and greet you as response. Try the following CLI\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --tl cadence-samples-worker \\\n  --et 60 \\\n  --workflow_type cadence_samples.SimpleSignalWorkflow\n```\n\nVerify that your workflow started. Your can find your worklow by looking at the \"Workflow type\" column.\n\nIf this is your first sample, please refer to [HelloWorkflow sample](https://github.com/cadence-workflow/cadence-samples/tree/master/new_samples/hello_world) about how to view your workflows.\n\n\n### Signal your workflow\n\nThis workflow will need a signal to complete successfully. Below is how you can send a signal. In this example, we are sending a `bool` value `true` (JSON formatted) via the signal called `complete`\n\n```bash\ncadence --domain cadence-samples \\\n  workflow signal \\\n  --wid <workflow_id> \\\n  --name complete \\\n  --input 'true'\n```\n\n## References\n\n* The website: https://cadenceworkflow.io\n* Cadence's server: https://github.com/uber/cadence\n* Cadence's Go client: https://github.com/uber-go/cadence-client\n\n"
  },
  {
    "path": "new_samples/signal/generator/README.md",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# Sample Generator\n\nThis folder is NOT part of the actual sample. It exists only for contributors who work on this sample. Please disregard it if you are trying to learn about Cadence.\n\nTo create a better learning experience for Cadence users, each sample folder is designed to be self contained. Users can view every part of writing and running workflows, including:\n\n* Cadence client initialization\n* Worker with workflow and activity registrations\n* Workflow starter\n* and the workflow code itself\n\nSome samples may have more or fewer parts depending on what they need to demonstrate.\n\nIn most cases, the workflow code (e.g. `workflow.go`) is the part that users care about. The rest is boilerplate needed to run that workflow. For each sample folder, the workflow code should be written by hand. The boilerplate can be generated. Keeping all parts inside one folder gives early learners more value because they can see everything together rather than jumping across directories.\n\n## Contributing\n\n* When creating a new sample, follow the steps mentioned in the README file in the main samples folder.\n* To update the sample workflow code, edit the workflow file directly.\n* To update the worker, client, or other boilerplate logic, edit the generator file. If your change applies to all samples, update the common generator file inside the `template` folder. Edit the generator file in this folder only when the change should affect this sample alone.\n* When you are done run the following command in the generator folder\n\n```bash\ngo run .\n```\n"
  },
  {
    "path": "new_samples/signal/generator/README_specific.md",
    "content": "## Simple Signal Workflow\n\nThis workflow takes an input message and greet you as response. Try the following CLI\n\n```bash\ncadence --domain cadence-samples \\\n  workflow start \\\n  --tl cadence-samples-worker \\\n  --et 60 \\\n  --workflow_type cadence_samples.SimpleSignalWorkflow\n```\n\nVerify that your workflow started. Your can find your worklow by looking at the \"Workflow type\" column.\n\nIf this is your first sample, please refer to [HelloWorkflow sample](https://github.com/cadence-workflow/cadence-samples/tree/master/new_samples/hello_world) about how to view your workflows.\n\n\n### Signal your workflow\n\nThis workflow will need a signal to complete successfully. Below is how you can send a signal. In this example, we are sending a `bool` value `true` (JSON formatted) via the signal called `complete`\n\n```bash\ncadence --domain cadence-samples \\\n  workflow signal \\\n  --wid <workflow_id> \\\n  --name complete \\\n  --input 'true'\n```\n"
  },
  {
    "path": "new_samples/signal/generator/generate.go",
    "content": "package main\n\nimport \"github.com/uber-common/cadence-samples/new_samples/template\"\n\nfunc main() {\n\t// Define the data for HelloWorld\n\tdata := template.TemplateData{\n\t\tSampleName: \"Signal Workflow\",\n\t\tWorkflows:  []string{\"SimpleSignalWorkflow\"},\n\t\tActivities: []string{\"SimpleSignalActivity\"},\n\t}\n\n\ttemplate.GenerateAll(data)\n}\n\n// Implement custom generator below"
  },
  {
    "path": "new_samples/signal/main.go",
    "content": "// THIS IS A GENERATED FILE\n// PLEASE DO NOT EDIT\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc main() {\n\tStartWorker()\n\n\tdone := make(chan os.Signal, 1)\n\tsignal.Notify(done, syscall.SIGINT)\n\tfmt.Println(\"Cadence worker started, press ctrl+c to terminate...\")\n\t<-done\n}\n"
  },
  {
    "path": "new_samples/signal/simple_signal_workflow.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/cadence/activity\"\n\t\"strconv\"\n\t\"time\"\n\t\"go.uber.org/zap\"\n)\n\nconst (\n\tCompleteSignalChan = \"complete\"\n)\n\nfunc SimpleSignalWorkflow(ctx workflow.Context) error {\n\tao := workflow.ActivityOptions{\n\t\tScheduleToStartTimeout: time.Minute * 60,\n\t\tStartToCloseTimeout:    time.Minute * 60,\n\t}\n\tctx = workflow.WithActivityOptions(ctx, ao)\n\tlogger := workflow.GetLogger(ctx)\n\tlogger.Info(\"SimpleSignalWorkflow started\")\n\n\tvar complete bool\n\tcompleteChan := workflow.GetSignalChannel(ctx, CompleteSignalChan)\n\tfor {\n\t\ts := workflow.NewSelector(ctx)\n\t\ts.AddReceive(completeChan, func(ch workflow.Channel, ok bool) {\n\t\t\tif ok {\n\t\t\t\tch.Receive(ctx, &complete)\n\t\t\t}\n\t\t\tlogger.Info(\"Signal input: \" + strconv.FormatBool(complete))\n\t\t})\n\t\ts.Select(ctx)\n\n\t\tvar result string\n\t\terr := workflow.ExecuteActivity(ctx, SimpleSignalActivity, complete).Get(ctx, &result)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tlogger.Info(\"Activity result: \" + result)\n\t\tif complete {\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\nfunc SimpleSignalActivity(ctx context.Context, complete bool) (string, error) {\n\tlogger := activity.GetLogger(ctx)\n\tlogger.Info(\"SimpleSignalActivity started, a new signal has been received\", zap.Bool(\"complete\", complete))\n\tif complete {\n\t\treturn \"Workflow will complete now\", nil\n\t}\n\treturn \"Workflow will continue to run\", nil\n}\n"
  },
  {
    "path": "new_samples/signal/worker.go",
    "content": "// THIS IS A GENERATED FILE\n// PLEASE DO NOT EDIT\n\n// Package worker implements a Cadence worker with basic configurations.\npackage main\n\nimport (\n\t\"github.com/uber-go/tally\"\n\tapiv1 \"github.com/uber/cadence-idl/go/proto/api/v1\"\n\t\"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/compatibility\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/yarpc\"\n\t\"go.uber.org/yarpc/peer\"\n\tyarpchostport \"go.uber.org/yarpc/peer/hostport\"\n\t\"go.uber.org/yarpc/transport/grpc\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nconst (\n\tHostPort = \"127.0.0.1:7833\"\n\tDomain   = \"cadence-samples\"\n\t// TaskListName identifies set of client workflows, activities, and workers.\n\t// It could be your group or client or application name.\n\tTaskListName   = \"cadence-samples-worker\"\n\tClientName     = \"cadence-samples-worker\"\n\tCadenceService = \"cadence-frontend\"\n)\n\n// StartWorker creates and starts a basic Cadence worker.\nfunc StartWorker() {\n\tlogger, cadenceClient := BuildLogger(), BuildCadenceClient()\n\tworkerOptions := worker.Options{\n\t\tLogger:       logger,\n\t\tMetricsScope: tally.NewTestScope(TaskListName, nil),\n\t}\n\n\tw := worker.New(\n\t\tcadenceClient,\n\t\tDomain,\n\t\tTaskListName,\n\t\tworkerOptions)\n\t// workflow registration\n\tw.RegisterWorkflowWithOptions(SimpleSignalWorkflow, workflow.RegisterOptions{Name: \"cadence_samples.SimpleSignalWorkflow\"})\n\tw.RegisterActivityWithOptions(SimpleSignalActivity, activity.RegisterOptions{Name: \"cadence_samples.SimpleSignalActivity\"})\n\n\terr := w.Start()\n\tif err != nil {\n\t\tpanic(\"Failed to start worker: \" + err.Error())\n\t}\n\tlogger.Info(\"Started Worker.\", zap.String(\"worker\", TaskListName))\n\n}\n\nfunc BuildCadenceClient(dialOptions ...grpc.DialOption) workflowserviceclient.Interface {\n\tgrpcTransport := grpc.NewTransport()\n\t// Create a single peer chooser that identifies the host/port and configures\n\t// a gRPC dialer with TLS credentials\n\tmyChooser := peer.NewSingle(\n\t\tyarpchostport.Identify(HostPort),\n\t\tgrpcTransport.NewDialer(dialOptions...),\n\t)\n\toutbound := grpcTransport.NewOutbound(myChooser)\n\n\tdispatcher := yarpc.NewDispatcher(yarpc.Config{\n\t\tName: ClientName,\n\t\tOutbounds: yarpc.Outbounds{\n\t\t\tCadenceService: {Unary: outbound},\n\t\t},\n\t})\n\tif err := dispatcher.Start(); err != nil {\n\t\tpanic(\"Failed to start dispatcher: \" + err.Error())\n\t}\n\n\tclientConfig := dispatcher.ClientConfig(CadenceService)\n\n\t// Create a compatibility adapter that wraps proto-based YARPC clients\n\t// to provide a unified interface for domain, workflow, worker, and visibility APIs\n\treturn compatibility.NewThrift2ProtoAdapter(\n\t\tapiv1.NewDomainAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkflowAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkerAPIYARPCClient(clientConfig),\n\t\tapiv1.NewVisibilityAPIYARPCClient(clientConfig),\n\t)\n}\n\nfunc BuildLogger() *zap.Logger {\n\tconfig := zap.NewDevelopmentConfig()\n\tconfig.Level.SetLevel(zapcore.InfoLevel)\n\n\tvar err error\n\tlogger, err := config.Build()\n\tif err != nil {\n\t\tpanic(\"Failed to setup logger: \" + err.Error())\n\t}\n\n\treturn logger\n}\n"
  },
  {
    "path": "new_samples/template/README.tmpl",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# {{.SampleName}} Sample\n\n## Prerequisites\n\n0. Install Cadence CLI. See instruction [here](https://cadenceworkflow.io/docs/cli/).\n1. Run the Cadence server:\n    1. Clone the [Cadence](https://github.com/cadence-workflow/cadence) repository if you haven't done already: `git clone https://github.com/cadence-workflow/cadence.git`\n    2. Run `docker compose -f docker/docker-compose.yml up` to start Cadence server\n    3. See more details at https://github.com/uber/cadence/blob/master/README.md\n2. Once everything is up and running in Docker, open [localhost:8088](localhost:8088) to view Cadence UI.\n3. Register the `cadence-samples` domain:\n\n```bash\ncadence --domain cadence-samples domain register\n```\n\nRefresh the [domains page](http://localhost:8088/domains) from step 2 to verify `cadence-samples` is registered.\n\n## Steps to run sample\n\nInside the folder this sample is defined, run the following command:\n\n```bash\ngo run .\n```\n\nThis will call the main function in main.go which starts the worker, which will be execute the sample workflow code\n\n"
  },
  {
    "path": "new_samples/template/README_generator.tmpl",
    "content": "<!-- THIS IS A GENERATED FILE -->\n<!-- PLEASE DO NOT EDIT -->\n\n# Sample Generator\n\nThis folder is NOT part of the actual sample. It exists only for contributors who work on this sample. Please disregard it if you are trying to learn about Cadence.\n\nTo create a better learning experience for Cadence users, each sample folder is designed to be self contained. Users can view every part of writing and running workflows, including:\n\n* Cadence client initialization\n* Worker with workflow and activity registrations\n* Workflow starter\n* and the workflow code itself\n\nSome samples may have more or fewer parts depending on what they need to demonstrate.\n\nIn most cases, the workflow code (e.g. `workflow.go`) is the part that users care about. The rest is boilerplate needed to run that workflow. For each sample folder, the workflow code should be written by hand. The boilerplate can be generated. Keeping all parts inside one folder gives early learners more value because they can see everything together rather than jumping across directories.\n\n## Contributing\n\n* When creating a new sample, follow the steps mentioned in the README file in the main samples folder.\n* To update the sample workflow code, edit the workflow file directly.\n* To update the worker, client, or other boilerplate logic, edit the generator file. If your change applies to all samples, update the common generator file inside the `template` folder. Edit the generator file in this folder only when the change should affect this sample alone.\n* When you are done run the following command in the generator folder\n\n```bash\ngo run .\n```\n"
  },
  {
    "path": "new_samples/template/README_references.tmpl",
    "content": "\n## References\n\n* The website: https://cadenceworkflow.io\n* Cadence's server: https://github.com/uber/cadence\n* Cadence's Go client: https://github.com/uber-go/cadence-client\n\n"
  },
  {
    "path": "new_samples/template/generator.go",
    "content": "package template\n\nimport (\n\t\"os\"\n\t\"text/template\"\n)\n\ntype TemplateData struct {\n\tSampleName string\n\tWorkflows  []string\n\tActivities []string\n}\n\nfunc GenerateAll(data TemplateData) {\n\tGenerateWorker(data)\n\tGenerateMain(data)\n\tGenerateSampleReadMe(data)\n\tGenerateGeneratorReadMe(data)\n}\n\nfunc GenerateWorker(data TemplateData) {\n\tGenerateFile(\"../../template/worker.tmpl\", \"../worker.go\", data)\n\tprintln(\"Generated worker.go\")\n}\n\nfunc GenerateMain(data TemplateData) {\n\tGenerateFile(\"../../template/main.tmpl\", \"../main.go\", data)\n\tprintln(\"Generated main.go\")\n}\n\nfunc GenerateSampleReadMe(data TemplateData) {\n\tinputs := []string{\"../../template/README.tmpl\", \"README_specific.md\", \"../../template/README_references.tmpl\"}\n\tGenerateREADME(inputs, \"../README.md\", data)\n}\n\nfunc GenerateGeneratorReadMe(data TemplateData) {\n\tGenerateFile(\"../../template/README_generator.tmpl\", \"README.md\", data)\n\tprintln(\"Generated README.md\")\n}\n\nfunc GenerateFile(templatePath, outputPath string, data TemplateData) {\n\ttmpl, err := template.ParseFiles(templatePath)\n\tif err != nil {\n\t\tpanic(\"Failed to parse template \" + templatePath + \": \" + err.Error())\n\t}\n\n\tf, err := os.Create(outputPath)\n\tif err != nil {\n\t\tpanic(\"Failed to create output file \" + outputPath + \": \" + err.Error())\n\t}\n\tdefer f.Close()\n\n\terr = tmpl.Execute(f, data)\n\tif err != nil {\n\t\tpanic(\"Failed to execute template: \" + err.Error())\n\t}\n}\n\nfunc GenerateREADME(inputs []string, outputPath string, data TemplateData) {\n\t// Create output file\n\tf, err := os.Create(outputPath)\n\tif err != nil {\n\t\tpanic(\"Failed to create README file: \" + err.Error())\n\t}\n\tdefer f.Close()\n\n\tfor _, input := range inputs {\n\t\ttmpl, err := template.ParseFiles(input)\n\t\tif err != nil {\n\t\t\tpanic(\"Failed to parse README template: \" + err.Error())\n\t\t}\n\n\t\terr = tmpl.Execute(f, data)\n\t\tif err != nil {\n\t\t\tpanic(input + \": Failed to append README content: \" + err.Error())\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "new_samples/template/main.tmpl",
    "content": "// THIS IS A GENERATED FILE\n// PLEASE DO NOT EDIT\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nfunc main() {\n\tStartWorker()\n\n\tdone := make(chan os.Signal, 1)\n\tsignal.Notify(done, syscall.SIGINT)\n\tfmt.Println(\"Cadence worker started, press ctrl+c to terminate...\")\n\t<-done\n}\n"
  },
  {
    "path": "new_samples/template/worker.tmpl",
    "content": "// THIS IS A GENERATED FILE\n// PLEASE DO NOT EDIT\n\n// Package worker implements a Cadence worker with basic configurations.\npackage main\n\nimport (\n\t\"github.com/uber-go/tally\"\n\tapiv1 \"github.com/uber/cadence-idl/go/proto/api/v1\"\n\t\"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient\"\n\t\"go.uber.org/cadence/activity\"\n\t\"go.uber.org/cadence/compatibility\"\n\t\"go.uber.org/cadence/worker\"\n\t\"go.uber.org/cadence/workflow\"\n\t\"go.uber.org/yarpc\"\n\t\"go.uber.org/yarpc/peer\"\n\tyarpchostport \"go.uber.org/yarpc/peer/hostport\"\n\t\"go.uber.org/yarpc/transport/grpc\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nconst (\n\tHostPort = \"127.0.0.1:7833\"\n\tDomain   = \"cadence-samples\"\n\t// TaskListName identifies set of client workflows, activities, and workers.\n\t// It could be your group or client or application name.\n\tTaskListName   = \"cadence-samples-worker\"\n\tClientName     = \"cadence-samples-worker\"\n\tCadenceService = \"cadence-frontend\"\n)\n\n// StartWorker creates and starts a basic Cadence worker.\nfunc StartWorker() {\n\tlogger, cadenceClient := BuildLogger(), BuildCadenceClient()\n\tworkerOptions := worker.Options{\n\t\tLogger:       logger,\n\t\tMetricsScope: tally.NewTestScope(TaskListName, nil),\n\t}\n\n\tw := worker.New(\n\t\tcadenceClient,\n\t\tDomain,\n\t\tTaskListName,\n\t\tworkerOptions)\n\t// workflow registration\n\t{{- range .Workflows}}\n\tw.RegisterWorkflowWithOptions({{.}}, workflow.RegisterOptions{Name: \"cadence_samples.{{.}}\"})\n\t{{- end}}\n\t{{- range .Activities}}\n\tw.RegisterActivityWithOptions({{.}}, activity.RegisterOptions{Name: \"cadence_samples.{{.}}\"})\n\t{{- end}}\n\n\terr := w.Start()\n\tif err != nil {\n\t\tpanic(\"Failed to start worker: \" + err.Error())\n\t}\n\tlogger.Info(\"Started Worker.\", zap.String(\"worker\", TaskListName))\n\n}\n\nfunc BuildCadenceClient(dialOptions ...grpc.DialOption) workflowserviceclient.Interface {\n\tgrpcTransport := grpc.NewTransport()\n\t// Create a single peer chooser that identifies the host/port and configures\n\t// a gRPC dialer with TLS credentials\n\tmyChooser := peer.NewSingle(\n\t\tyarpchostport.Identify(HostPort),\n\t\tgrpcTransport.NewDialer(dialOptions...),\n\t)\n\toutbound := grpcTransport.NewOutbound(myChooser)\n\n\tdispatcher := yarpc.NewDispatcher(yarpc.Config{\n\t\tName: ClientName,\n\t\tOutbounds: yarpc.Outbounds{\n\t\t\tCadenceService: {Unary: outbound},\n\t\t},\n\t})\n\tif err := dispatcher.Start(); err != nil {\n\t\tpanic(\"Failed to start dispatcher: \" + err.Error())\n\t}\n\n\tclientConfig := dispatcher.ClientConfig(CadenceService)\n\n\t// Create a compatibility adapter that wraps proto-based YARPC clients\n\t// to provide a unified interface for domain, workflow, worker, and visibility APIs\n\treturn compatibility.NewThrift2ProtoAdapter(\n\t\tapiv1.NewDomainAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkflowAPIYARPCClient(clientConfig),\n\t\tapiv1.NewWorkerAPIYARPCClient(clientConfig),\n\t\tapiv1.NewVisibilityAPIYARPCClient(clientConfig),\n\t)\n}\n\nfunc BuildLogger() *zap.Logger {\n\tconfig := zap.NewDevelopmentConfig()\n\tconfig.Level.SetLevel(zapcore.InfoLevel)\n\n\tvar err error\n\tlogger, err := config.Build()\n\tif err != nil {\n\t\tpanic(\"Failed to setup logger: \" + err.Error())\n\t}\n\n\treturn logger\n}\n"
  },
  {
    "path": "python_sdk_samples/.python-version",
    "content": "3.13\n"
  },
  {
    "path": "python_sdk_samples/README.md",
    "content": "# Cadence Python SDK Samples\n\nAll samples under this folder demonstrate how to use Python SDK effectively.\n\n## 🚀 Quick Start\n\n1. We use uv to install dependencies of all samples\n\nRefer to [UV installation Guide](https://docs.astral.sh/uv/getting-started/installation/)\n\n2. build all samples\n```bash\ncd python_sdk_samples\nuv sync\n```\n\nThis downloads all dependencies so `uv run` will have all the dependent packages\n\n3. Start Cadence Server\n\n```bash\ncurl -LO https://raw.githubusercontent.com/cadence-workflow/cadence/refs/heads/master/docker/docker-compose.yml && docker-compose up --wait\n```\n\nThis downloads and starts all required dependencies including Cadence server, database, and [Cadence Web UI](https://github.com/uber/cadence-web). You can view your sample workflows at [http://localhost:8088](http://localhost:8088).\n\n4. **run one sample**:\n\n```bash\nuv run python -m openai_samples.agent_handoffs.main\n```\n"
  },
  {
    "path": "python_sdk_samples/__init__.py",
    "content": ""
  },
  {
    "path": "python_sdk_samples/openai_samples/__init__.py",
    "content": ""
  },
  {
    "path": "python_sdk_samples/openai_samples/agent_handoffs/README.md",
    "content": "# What\n\nThis demo shows how to run OpenAI agents in a durable way (retries, reset to a check point). Specifically, we show the execution of a multi-agent system with handoffs.\n\n# Setup OpenAI API keys\n\nMake sure the OPENAI_API_KEY environment variable is set. See details for best practices.\nhttps://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety\n\n# Setup Cadence Server\n\nRefer to step 3 of the [Quick Start](../../README.md) in `python_sdk_samples/README.md` for instructions on starting the Cadence Server:\n\n```bash\ncurl -LO https://raw.githubusercontent.com/cadence-workflow/cadence/refs/heads/master/docker/docker-compose.yml && docker-compose up --wait\n```\n\n# Start Agent workers\n\n```\ncd python_sdk_samples\nuv sync\nuv run python -m openai_samples.agent_handoffs.main\n```\n\n# Trigger Agent Run\n\nRun Cadence CLI command\n\n```\ncadence --domain default workflow start \\\n--workflow_type BookTripAgentWorkflow \\\n--tasklist agent-task-list \\\n--execution_timeout 30 \\\n--input '\"Book a trip for me from Uber Seattle Office to Uber San Francisco Office tomorrow at 10:00 AM\"'\n```\n\nOr click start workflow in the [cadence-web](http://localhost:8088/domains/default/cluster0/workflows)\n![Start Workflow Screenshot](images/start_workflow.png)\n\n# View Agent Run Result\n\n![Agent Workflow Result Screenshot](images/agent_workflow_result.png)\n"
  },
  {
    "path": "python_sdk_samples/openai_samples/agent_handoffs/__init__.py",
    "content": ""
  },
  {
    "path": "python_sdk_samples/openai_samples/agent_handoffs/book_trip_agent.py",
    "content": "import cadence\nfrom agents import Agent, function_tool, Runner, RunConfig\n\nfrom .tools import book_flight, book_uber\n\nagent_registry = cadence.Registry()\n\n@agent_registry.workflow(name=\"BookTripAgentWorkflow\")\nclass BookTripAgentWorkflow:\n\n    @cadence.workflow.run\n    async def run(self, input: str) -> str:\n\n        long_trip_agent =Agent(\n            name = \"Plan Long Trip Agent\",\n            model = \"gpt-4o-mini\",\n            instructions= \"\"\"\n            Book a flight from start address to destination address.\n            Use Uber to connect local address to airport or vice versa.\n            \"\"\",\n            tools = [\n                function_tool(book_flight),\n                function_tool(book_uber),\n            ],\n        )\n\n        short_trip_agent =Agent(\n            name = \"Plan Short Trip Agent\",\n            instructions= \"\"\"\n            Book a Uber ride from start address to destination address.\n            \"\"\",\n            model = \"gpt-4o-mini\",\n            tools = [\n                function_tool(book_uber),\n            ],\n        )\n\n        # define agent using OpenAI SDK as usual\n        agent =Agent(\n            name = \"Book Trip Agent\",\n            instructions = \"\"\"\n            You are a trip planner. You can plan short or long trips.\n            \"\"\",\n            model = \"gpt-4o-mini\",\n            handoffs = [\n                short_trip_agent,\n                long_trip_agent,\n            ],\n        )\n        result = await Runner.run(agent, input, run_config=RunConfig(\n                tracing_disabled=True,\n            ))\n        return result.final_output\n"
  },
  {
    "path": "python_sdk_samples/openai_samples/agent_handoffs/main.py",
    "content": "import asyncio\nimport logging\nimport signal\nimport cadence\nfrom cadence.contrib.openai import PydanticDataConverter, cadence_registry\n\nfrom .tools import tools_registry\nfrom .book_trip_agent import agent_registry\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\n\n\nasync def main():\n    # start Cadence worker\n    worker = cadence.worker.Worker(\n        cadence.Client(\n            domain=\"default\",\n            target=\"localhost:7833\",\n            data_converter=PydanticDataConverter(),\n        ),\n        \"agent-task-list\",\n        cadence.Registry.of(\n            cadence_registry.cadence_registry,\n            tools_registry,\n            agent_registry),\n    )\n\n    # start BookFlightAgentWorkflow\n    async with worker:\n        logger.info(\"Worker started. Go to http://localhost:8088/domains/default/cluster0/workflows to start an agent run.\")\n        logger.info(\"Sample input: Book a trip for me from Uber Seattle Office to Uber San Francisco Office tomorrow at 10:00 AM\")\n        shutdown_event = asyncio.Event()\n        loop = asyncio.get_running_loop()\n        for sig in (signal.SIGTERM, signal.SIGINT):\n            loop.add_signal_handler(sig, shutdown_event.set)\n        logger.info(\"Press Ctrl+C to stop the worker.\")\n\n        await shutdown_event.wait()\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n"
  },
  {
    "path": "python_sdk_samples/openai_samples/agent_handoffs/tools.py",
    "content": "import cadence\nfrom datetime import datetime\nfrom dataclasses import dataclass\n\ntools_registry = cadence.Registry()\n\n@dataclass\nclass Flight:\n    from_city: str\n    to_city: str\n    departure_date: datetime\n    price: float\n    airline: str\n    flight_number: str\n    seat_number: str\n\n@tools_registry.activity(name=\"book_flight\")\nasync def book_flight(from_city: str, to_city: str, departure_date: datetime) -> Flight:\n    \"\"\"\n    Book a Flight tool: a pure mock for demo purposes\n    \"\"\"\n    return Flight(from_city=from_city, to_city=to_city, departure_date=departure_date, price=100, airline=\"United\", flight_number=\"123456\", seat_number=\"12A\")\n\n@dataclass\nclass UberTrip:\n    from_address: str\n    to_address: str\n    passengers: int\n    price: float\n    driver_name: str\n    driver_phone: str\n    driver_car: str\n    driver_car_plate: str\n    driver_car_color: str\n\n@tools_registry.activity(name=\"book_uber\")\nasync def book_uber(from_address: str, to_address: str, passengers: int) -> UberTrip:\n    \"\"\"\n    Book a Uber ride from start address to the destination address. default passengers is 1.\n    \"\"\"\n    return UberTrip(from_address=from_address, to_address=to_address, passengers=passengers, price=100, driver_name=\"John Doe\", driver_phone=\"1234567890\", driver_car=\"Toyota\", driver_car_plate=\"1234567890\", driver_car_color=\"Red\")\n"
  },
  {
    "path": "python_sdk_samples/pyproject.toml",
    "content": "[project]\nname = \"python-sdk-samples\"\nversion = \"0.1.0\"\ndescription = \"Add your description here\"\nreadme = \"README.md\"\nrequires-python = \">=3.13\"\ndependencies = [\n    \"cadence-python-client[openai]>=0.2.1\",\n    \"ruff>=0.15.10\",\n]\n"
  }
]