flowstash-cli 0.1.3__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flowstash_cli-0.1.3 → flowstash_cli-0.2.0}/PKG-INFO +1 -1
- {flowstash_cli-0.1.3 → flowstash_cli-0.2.0}/pyproject.toml +3 -3
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/commands/webhook.py +1 -1
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/main.py +1 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/AGENTS.md +68 -10
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/README.md +4 -4
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_api_main.py +2 -2
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_src/_api/_routes/webhooks.py +3 -3
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_src/_shared/clients/client.py +1 -1
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_src/_shared/tasks/sharedTasks.py +2 -2
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_src/_worker/tasks/tasks.py +2 -2
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_worker_main.py +2 -2
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/__init__.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/commands/__init__.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/commands/auth.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/commands/build.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/commands/deploy.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/commands/project.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/commands/run.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/core/__init__.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/core/api_client.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/core/auth_server.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/core/builder.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/core/config.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/core/docker_utils.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/core/patcher.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_.dockerignore +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_.flowstash +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_config/[env]/(backend-asyncio)/backend.yaml +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_config/[env]/(backend-dramatiq)/backend.yaml +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_config/[env]/(backend-managed)/backend.yaml +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_config/[env]/_backend.yaml +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_config/shared/.env +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_config/shared/backend.yaml +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_config/shared/clients/demoClient.yaml +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_config/shared/clients.yaml +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_deployment/[env]/(backend-asyncio)/docker-compose.yaml +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_deployment/[env]/(backend-dramatiq)/docker-compose.yaml +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_deployment/[env]/.env +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_deployment/shared/.env +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_deployment/shared/api.Dockerfile +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_deployment/shared/worker.Dockerfile +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_pyproject.toml +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_src/_api/__init__.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_src/_shared/__init__.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_src/_shared/models/models.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_src/_worker/__init__.py +0 -0
- {flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/ui/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "flowstash-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "CLI for the flowstash Managed Platform"
|
|
5
5
|
authors = [{name = "juraj.bezdek@gmail.com", email = "juraj.bezdek@gmail.com"}]
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -18,11 +18,11 @@ dependencies = [
|
|
|
18
18
|
]
|
|
19
19
|
|
|
20
20
|
[project.scripts]
|
|
21
|
-
flowstash = "flowstash.main:app"
|
|
21
|
+
flowstash = "flowstash.cli.main:app"
|
|
22
22
|
|
|
23
23
|
[tool.poetry]
|
|
24
24
|
packages = [
|
|
25
|
-
{ include = "flowstash" }
|
|
25
|
+
{ include = "flowstash", from = "src" }
|
|
26
26
|
]
|
|
27
27
|
|
|
28
28
|
[tool.poetry.dependencies]
|
|
@@ -13,13 +13,13 @@ flowstash integrations revolve around the following core ideas:
|
|
|
13
13
|
|
|
14
14
|
## Bringing Data In: Ingress
|
|
15
15
|
|
|
16
|
-
You can trigger your pipelines through two main ingress decorators provided by `
|
|
16
|
+
You can trigger your pipelines through two main ingress decorators provided by `flowstash.ingress`:
|
|
17
17
|
|
|
18
18
|
### 1. Webhooks (`@ingress.webhook`)
|
|
19
19
|
Use webhooks when an external system can push events to your application. This decorator registers the handler as part of the integration but leaves the exact handling logic to you.
|
|
20
20
|
|
|
21
21
|
```python
|
|
22
|
-
from
|
|
22
|
+
from flowstash.ingress import ingress
|
|
23
23
|
|
|
24
24
|
@router.post("/webhook/slack")
|
|
25
25
|
@ingress.webhook(pipeline="slack.messages", integration="slack")
|
|
@@ -34,7 +34,7 @@ Use polling when you need to fetch data on a schedule. This decorator acts as a
|
|
|
34
34
|
The state is automatically saved for you as long as the function executes without exceptions.
|
|
35
35
|
|
|
36
36
|
```python
|
|
37
|
-
from
|
|
37
|
+
from flowstash.ingress import ingress
|
|
38
38
|
from datetime import datetime
|
|
39
39
|
|
|
40
40
|
@ingress.poll(pipeline="slack.messages", integration="slack", schedule="*/5 * * * *")
|
|
@@ -51,6 +51,64 @@ async def poll_slack(state: dict):
|
|
|
51
51
|
state["since"] = datetime.now()
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
## State Management: The State Machine Principle
|
|
55
|
+
|
|
56
|
+
Integrations often need to remember things between runs. flowstash treats every integration run as a transition in a **state machine**. You load the current state, perform your logic, and save the updated state.
|
|
57
|
+
|
|
58
|
+
### The `State` Facade
|
|
59
|
+
|
|
60
|
+
The `State` facade (accessible via `from flowstash.integration import State`) provides a centralized interface for persistent storage. It automatically handles namespacing, serialization, and TTLs, so you can focus on the data.
|
|
61
|
+
|
|
62
|
+
#### Scopes
|
|
63
|
+
State is automatically isolated into three logical scopes based on the active context:
|
|
64
|
+
- **`integration` (Default)**: Shared across all tasks and pipelines in a specific integration (e.g., `slack`). Use for global settings or cross-pipeline flags.
|
|
65
|
+
- **`pipeline`**: Shared across all tasks in a specific pipeline (e.g., `slack.sync_users`).
|
|
66
|
+
- **`ingress`**: Private state for a specific ingress entrypoint (e.g., a specific poll timer). This is the default scope for the `state` dict injected into `@ingress.poll`.
|
|
67
|
+
|
|
68
|
+
#### Basic Usage
|
|
69
|
+
```python
|
|
70
|
+
from flowstash.integration import State
|
|
71
|
+
|
|
72
|
+
@integration_task(integration="demo", pipeline="example")
|
|
73
|
+
async def my_stateful_task():
|
|
74
|
+
# 1. Get current state (scoped to integration:demo by default)
|
|
75
|
+
# Returns None or the decoded value (JSON objects are automatically parsed)
|
|
76
|
+
last_processed = State.get("last_id") or 0
|
|
77
|
+
|
|
78
|
+
# ... perform logic ...
|
|
79
|
+
|
|
80
|
+
# 2. Update state (persists to the configured backend: Redis, SQLite, Managed, etc.)
|
|
81
|
+
State.set("last_id", 123)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Auto-Saving in Ingress
|
|
85
|
+
In `@ingress.poll`, state management is handled for you. The `state` dictionary is automatically loaded before your function runs and persisted back to the `ingress` scope precisely when your function returns successfully.
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
@ingress.poll(pipeline="demo", integration="acme", schedule="* * * * *")
|
|
89
|
+
async def poll_handler(state: dict):
|
|
90
|
+
# This 'state' is already loaded for you
|
|
91
|
+
cursor = state.get("cursor", "init")
|
|
92
|
+
|
|
93
|
+
# ... fetching data ...
|
|
94
|
+
|
|
95
|
+
# Changes to this dict are automatically saved on a successful return
|
|
96
|
+
state["cursor"] = "next_page_token"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Context Bound
|
|
100
|
+
`State` is fully context-aware. You don't need to pass around tenant IDs or integration names. When you are inside an `@integration_task`, `@integration_step` or an `@ingress` handler, `State` already knows where to store the data.
|
|
101
|
+
|
|
102
|
+
If you need to use `State` in a CLI script or a test outside of a managed run, you can bind it manually:
|
|
103
|
+
```python
|
|
104
|
+
from flowstash.context import integration_context
|
|
105
|
+
|
|
106
|
+
with integration_context(integration="my-int", integration_pipeline="pipe"):
|
|
107
|
+
# Inside this block, State is bound to "my-int" and "pipe"
|
|
108
|
+
State.set("setup_complete", True)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
|
|
54
112
|
## Making External Requests: Clients
|
|
55
113
|
|
|
56
114
|
flowstash makes it easy to interact with external APIs via Clients. They handle setting Base URLs, automatic Authentication headers, observability tracing, and defaults out of the box.
|
|
@@ -64,11 +122,11 @@ pattern: "*.yaml"
|
|
|
64
122
|
You can simply define `clients/slackClient.yaml` and the system will expose it to your tasks.
|
|
65
123
|
|
|
66
124
|
### Using a Client
|
|
67
|
-
Once configured, you can retrieve the standard HTTP client (from the `
|
|
125
|
+
Once configured, you can retrieve the standard HTTP client (from the `flowstash.clients` package) anywhere.
|
|
68
126
|
|
|
69
127
|
```python
|
|
70
|
-
from
|
|
71
|
-
from
|
|
128
|
+
from flowstash.clients import get_client
|
|
129
|
+
from flowstash.decorators import integration_task
|
|
72
130
|
|
|
73
131
|
# Load the client configuration
|
|
74
132
|
slack_client = get_client("slackClient")
|
|
@@ -83,7 +141,7 @@ async def send_slack_message_task(message: str):
|
|
|
83
141
|
If you have an API you interact with heavily, you can define a custom typed client. This encapsulates specific API routes for a better developer experience.
|
|
84
142
|
|
|
85
143
|
```python
|
|
86
|
-
from
|
|
144
|
+
from flowstash.clients import HttpClient, client
|
|
87
145
|
from typing import List
|
|
88
146
|
|
|
89
147
|
# Extending HttpClient and registering with the @client decorator
|
|
@@ -113,8 +171,8 @@ The framework resolves deduplication using a unique combination of `(integration
|
|
|
113
171
|
It also seamlessly supports feeding massive data payloads -- large objects (> 5KB) are automatically offloaded to a BlobStore.
|
|
114
172
|
|
|
115
173
|
```python
|
|
116
|
-
from
|
|
117
|
-
from
|
|
174
|
+
from flowstash.pipelines.records_feed import RecordsFeed
|
|
175
|
+
from flowstash.pipelines.records_model import RecordData
|
|
118
176
|
|
|
119
177
|
# Retrieve your specific feed
|
|
120
178
|
feed = RecordsFeed.get(feed_id="slack.messages")
|
|
@@ -131,7 +189,7 @@ await feed.publish(RecordData(
|
|
|
131
189
|
To process the data asynchronously, decorate a function with `@feed_consumer`. This supports robust configurations like automatic batched receiving, delays, rate-limiting, and concurrency control.
|
|
132
190
|
|
|
133
191
|
```python
|
|
134
|
-
from
|
|
192
|
+
from flowstash.pipelines import feed_consumer, RecordData
|
|
135
193
|
|
|
136
194
|
@feed_consumer(
|
|
137
195
|
feed_id="slack.messages",
|
|
@@ -90,7 +90,7 @@ python worker.py
|
|
|
90
90
|
|
|
91
91
|
## flowstash Core Concepts Usage
|
|
92
92
|
|
|
93
|
-
This project is built on `
|
|
93
|
+
This project is built on `flowstash` – a core library providing robust context management, queue abstractions, and observability. Here is how you can utilize its components in your application:
|
|
94
94
|
|
|
95
95
|
### Integration Context
|
|
96
96
|
`IntegrationContext` propagates automatically through all your integration operations. It manages the current `run_id`, `integration`, and `current_record_key` to ensure all logs and traces are correctly correlated without manual passing.
|
|
@@ -102,7 +102,7 @@ This project is built on `flowstash_lib` – a core library providing robust con
|
|
|
102
102
|
### RecordsFeed & Consumers
|
|
103
103
|
Extract and ingest data efficiently using **RecordsFeed**:
|
|
104
104
|
```python
|
|
105
|
-
from
|
|
105
|
+
from flowstash.pipelines.records_feed import RecordsFeed, RecordData
|
|
106
106
|
|
|
107
107
|
feed = RecordsFeed.get(feed_id="your.pipeline")
|
|
108
108
|
feed.publish(RecordData(record_id="123", record_type="invoice", data={...}))
|
|
@@ -111,7 +111,7 @@ Records are automatically deduplicated and optionally ordered by timestamp.
|
|
|
111
111
|
|
|
112
112
|
Consume them robustly with **Feed Consumers**, which handle batching, rate-limiting, and parallel execution automatically:
|
|
113
113
|
```python
|
|
114
|
-
from
|
|
114
|
+
from flowstash.pipelines.consumer import feed_consumer
|
|
115
115
|
|
|
116
116
|
@feed_consumer(
|
|
117
117
|
feed_id="your.pipeline",
|
|
@@ -129,7 +129,7 @@ async def process_records(records: list[RecordData]):
|
|
|
129
129
|
Use the provided `logger` to naturally correlate your logs with the active context. The framework tracks the lifecycle of records intrinsically (from publishing to consumption) without manual `record_id` logging.
|
|
130
130
|
|
|
131
131
|
```python
|
|
132
|
-
from
|
|
132
|
+
from flowstash.observability.logging import logger
|
|
133
133
|
|
|
134
134
|
logger.info("Processing the invoice batch", details="step 1 completed")
|
|
135
135
|
```
|
{flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_api_main.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
from fastapi import Request
|
|
3
|
-
from
|
|
4
|
-
from
|
|
3
|
+
from flowstash.config.env_loader import load_config_dir
|
|
4
|
+
from flowstash.runtime.ingress.app import create_fastapi_app
|
|
5
5
|
import os
|
|
6
6
|
env = os.getenv("ENVIRONMENT", "dev")
|
|
7
7
|
# Load configuration relative to this file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
1
|
+
from flowstash import ingress
|
|
2
|
+
from flowstash.pipelines import RecordData
|
|
3
3
|
from fastapi import Request
|
|
4
|
-
from
|
|
4
|
+
from flowstash.pipelines.records_feed import RecordsFeed
|
|
5
5
|
|
|
6
6
|
@ingress.webhook(integration="demo", pipeline="process_user", path="/demo/process_user", method="POST")
|
|
7
7
|
async def process_user_webhook(request: Request):
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
1
|
+
from flowstash.decorators import integration_task
|
|
2
|
+
from flowstash.clients import get_client
|
|
3
3
|
|
|
4
4
|
@integration_task(integration="demo", integration_pipeline="adhoc_task")
|
|
5
5
|
async def adhoc_task(data: dict):
|
{flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_worker_main.py
RENAMED
|
@@ -2,8 +2,8 @@ import os
|
|
|
2
2
|
import asyncio
|
|
3
3
|
import logging
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from
|
|
6
|
-
from
|
|
5
|
+
from flowstash.config.env_loader import load_config_dir
|
|
6
|
+
from flowstash.runtime import initialize_runtime, run_worker
|
|
7
7
|
|
|
8
8
|
logging.basicConfig(level=logging.INFO)
|
|
9
9
|
|
|
File without changes
|
{flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/commands/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/core/docker_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
{flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_.dockerignore
RENAMED
|
File without changes
|
{flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_.flowstash
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flowstash_cli-0.1.3/flowstash → flowstash_cli-0.2.0/src/flowstash/cli}/templates/_pyproject.toml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|