{"id":28158491,"url":"https://github.com/indeedeng/iwf-python-samples","last_synced_at":"2025-05-15T09:20:03.390Z","repository":{"id":194468443,"uuid":"690891935","full_name":"indeedeng/iwf-python-samples","owner":"indeedeng","description":"samples for iWF Python SDK","archived":false,"fork":false,"pushed_at":"2025-04-23T13:25:10.000Z","size":530,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-05-07T23:40:07.079Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/indeedeng.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-13T05:09:33.000Z","updated_at":"2025-04-23T13:25:14.000Z","dependencies_parsed_at":"2025-03-14T06:25:13.012Z","dependency_job_id":"e1ccbefe-a70a-4537-b8ca-6826376ebe8f","html_url":"https://github.com/indeedeng/iwf-python-samples","commit_stats":null,"previous_names":["indeedeng/iwf-python-samples"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/indeedeng%2Fiwf-python-samples","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/indeedeng%2Fiwf-python-samples/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/indeedeng%2Fiwf-python-samples/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/indeedeng%2Fiwf-python-samples/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/indeedeng","download_url":"https://codeload.github.com/indeedeng/iwf-python-samples/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254310502,"owners_count":22049471,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-05-15T09:19:40.740Z","updated_at":"2025-05-15T09:20:03.379Z","avatar_url":"https://github.com/indeedeng.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# iwf-python-samples\n\nsamples for [iWF Python SDK](https://github.com/indeedeng/iwf-python-sdk)\n\n## Running Requirements\n\n* Python 3.9+\n* [iWF server](https://github.com/indeedeng/iwf#how-to-use)\n\n## How to run locally\n1. Start iWF server, using [docker](https://github.com/indeedeng/iwf?tab=readme-ov-file#using-all-in-one-docker-image) or [docker-compose](https://github.com/indeedeng/iwf?tab=readme-ov-file#using-docker-image--docker-compose)\n2. Install dependencies and run the sample code\n```\npoetry install\n# Run case 1\npoetry run python moneytransfer/main.py\n# or, run case 2\npoetry run python signup/main.py\n```\n\n## Case1: [Money transfer workflow/SAGA Patten](./moneytransfer)\n\nThis example shows how to transfer money from one account to another account.\nThe transfer involves multiple steps. When any step fails, the whole transfer is canceled with some compensation steps.\n\n## Case 2: User sign-up workflow\n\nA common use case that is almost everywhere -- new user sign-up/register a new account in a website/system.\nE.g. Amazon/Linkedin/Google/etc...\n\n### Use case requirements\n\n* User fills a form and submit to the system with email\n* System will send an email for verification\n* User will click the link in the email to verify the account\n* If not clicking, a reminder will be sent every X hours\n\n\u003cimg width=\"303\" alt=\"user case requirements\" src=\"https://github.com/indeedeng/iwf-python-sdk/assets/4523955/356a4284-b816-42d3-9e44-b371a91834e4\"\u003e\n\n### Some old solution\n\nWith some other existing technologies, you solve it using message queue(like SQS which has timer) + Database like below:\n\n\u003cimg width=\"309\" alt=\"old solution\" src=\"https://github.com/indeedeng/iwf-python-sdk/assets/4523955/49ef8846-9589-4a28-91bd-c575daf37dcf\"\u003e\n\n* Using visibility timeout for backoff retry\n* Need to re-enqueue the message for larger backoff\n* Using visibility timeout for durable timer\n* Need to re-enqueue the message for once to have 24 hours timer\n* Need to create one queue for every step\n* Need additional storage for waiting \u0026 processing ready signal\n* Only go to 3 or 4 if both conditions are met\n* Also need DLQ and build tooling around\n\n**It's complicated and hard to maintain and extend.**\n\n### New solution with iWF\n\nThe solution with iWF:\n\u003cimg width=\"752\" alt=\"iwf solution\" src=\"https://github.com/indeedeng/iwf-python-sdk/assets/4523955/4cec7742-a965-4a2d-868b-693ffba372fa\"\u003e\n* All in one single dependency\n* Natural to represent business\n* Builtin \u0026 rich support for operation tooling\n\nIt's so simple \u0026 easy to do that the [business logic code like this](./signup/signup_workflow.py) can be shown here!\n\n```python\nclass SubmitState(WorkflowState[Form]):\n    def execute(self, ctx: WorkflowContext, input: Form, command_results: CommandResults, persistence: Persistence,\n                communication: Communication,\n                ) -\u003e StateDecision:\n        persistence.set_data_attribute(data_attribute_form, input)\n        persistence.set_data_attribute(data_attribute_status, \"waiting\")\n        print(f\"API to send verification email to {input.email}\")\n        return StateDecision.single_next_state(VerifyState)\n\n\nclass VerifyState(WorkflowState[None]):\n    def wait_until(self, ctx: WorkflowContext, input: T, persistence: Persistence, communication: Communication,\n                   ) -\u003e CommandRequest:\n        return CommandRequest.for_any_command_completed(\n            TimerCommand.timer_command_by_duration(\n                timedelta(seconds=10)\n            ),  # use 10 seconds for demo\n            InternalChannelCommand.by_name(verify_channel),\n        )\n\n    def execute(self, ctx: WorkflowContext, input: T, command_results: CommandResults, persistence: Persistence,\n                communication: Communication,\n                ) -\u003e StateDecision:\n        form = persistence.get_data_attribute(data_attribute_form)\n        if (\n                command_results.internal_channel_commands[0].status\n                == ChannelRequestStatus.RECEIVED\n        ):\n            print(f\"API to send welcome email to {form.email}\")\n            return StateDecision.graceful_complete_workflow(\"done\")\n        else:\n            print(f\"API to send the a reminder email to {form.email}\")\n            return StateDecision.single_next_state(VerifyState)\n\n\nclass UserSignupWorkflow(ObjectWorkflow):\n    def get_workflow_states(self) -\u003e StateSchema:\n        return StateSchema.with_starting_state(SubmitState(), VerifyState())\n\n    def get_persistence_schema(self) -\u003e PersistenceSchema:\n        return PersistenceSchema.create(\n            PersistenceField.data_attribute_def(data_attribute_form, Form),\n            PersistenceField.data_attribute_def(data_attribute_status, str),\n            PersistenceField.data_attribute_def(data_attribute_verified_source, str),\n        )\n\n    def get_communication_schema(self) -\u003e CommunicationSchema:\n        return CommunicationSchema.create(\n            CommunicationMethod.internal_channel_def(verify_channel, None)\n        )\n\n    @rpc()\n    def verify(\n            self, source: str, persistence: Persistence, communication: Communication\n    ) -\u003e str:\n        status = persistence.get_data_attribute(data_attribute_status)\n        if status == \"verified\":\n            return \"already verified\"\n        persistence.set_data_attribute(data_attribute_status, \"verified\")\n        persistence.set_data_attribute(data_attribute_verified_source, source)\n        communication.publish_to_internal_channel(verify_channel)\n        return \"done\"\n```\n\nAnd the [application code](signup/main.py) will be simply interacting with the workflow like below:\n\n```python\n@flask_app.route(\"/signup/submit\")\ndef signup_submit():\n    username = request.args[\"username\"]\n    form = Form(\n        ...\n    )\n    client.start_workflow(UserSignupWorkflow, username, 3600, form)\n    return \"workflow started\"\n\n\n@flask_app.route(\"/signup/verify\")\ndef signup_verify():\n    username = request.args[\"username\"]\n    source = request.args[\"source\"]\n    return client.invoke_rpc(username, UserSignupWorkflow.verify, source)\n```\n\n### development tips\n\nWhen update iwf-python-sdk if the dependency is not updated:\n\n`poetry cache clear pypi --all \u0026\u0026 poetry update`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Findeedeng%2Fiwf-python-samples","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Findeedeng%2Fiwf-python-samples","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Findeedeng%2Fiwf-python-samples/lists"}