ddeutil-workflow 0.0.62__tar.gz → 0.0.64__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.
Files changed (70) hide show
  1. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/PKG-INFO +11 -57
  2. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/README.md +9 -56
  3. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/pyproject.toml +1 -0
  4. ddeutil_workflow-0.0.64/src/ddeutil/workflow/__about__.py +1 -0
  5. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/__init__.py +2 -31
  6. ddeutil_workflow-0.0.64/src/ddeutil/workflow/api/__init__.py +91 -0
  7. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/api/routes/__init__.py +0 -1
  8. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/api/routes/job.py +0 -1
  9. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/api/routes/logs.py +0 -2
  10. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/api/routes/workflows.py +0 -3
  11. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/conf.py +40 -36
  12. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/event.py +2 -1
  13. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/exceptions.py +0 -3
  14. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/reusables.py +104 -14
  15. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/stages.py +60 -52
  16. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/utils.py +4 -2
  17. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil_workflow.egg-info/PKG-INFO +11 -57
  18. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil_workflow.egg-info/SOURCES.txt +2 -12
  19. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil_workflow.egg-info/requires.txt +1 -0
  20. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_conf.py +28 -68
  21. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_reusables_call_tag.py +5 -1
  22. ddeutil_workflow-0.0.64/tests/test_reusables_func_model.py +158 -0
  23. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_stage_handler_exec.py +20 -3
  24. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_workflow_exec.py +1 -1
  25. ddeutil_workflow-0.0.62/src/ddeutil/workflow/__about__.py +0 -1
  26. ddeutil_workflow-0.0.62/src/ddeutil/workflow/api/__init__.py +0 -170
  27. ddeutil_workflow-0.0.62/src/ddeutil/workflow/api/routes/schedules.py +0 -141
  28. ddeutil_workflow-0.0.62/src/ddeutil/workflow/api/utils.py +0 -174
  29. ddeutil_workflow-0.0.62/src/ddeutil/workflow/scheduler.py +0 -813
  30. ddeutil_workflow-0.0.62/tests/test_schedule.py +0 -173
  31. ddeutil_workflow-0.0.62/tests/test_schedule_pending.py +0 -13
  32. ddeutil_workflow-0.0.62/tests/test_schedule_tasks.py +0 -82
  33. ddeutil_workflow-0.0.62/tests/test_schedule_workflow.py +0 -124
  34. ddeutil_workflow-0.0.62/tests/test_scheduler_control.py +0 -49
  35. ddeutil_workflow-0.0.62/tests/test_workflow_poke.py +0 -168
  36. ddeutil_workflow-0.0.62/tests/test_workflow_release.py +0 -153
  37. ddeutil_workflow-0.0.62/tests/test_workflow_task.py +0 -223
  38. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/LICENSE +0 -0
  39. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/setup.cfg +0 -0
  40. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/__cron.py +0 -0
  41. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/__main__.py +0 -0
  42. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/__types.py +0 -0
  43. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/api/logs.py +0 -0
  44. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/job.py +0 -0
  45. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/logs.py +0 -0
  46. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/params.py +0 -0
  47. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/result.py +0 -0
  48. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil/workflow/workflow.py +0 -0
  49. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
  50. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil_workflow.egg-info/entry_points.txt +0 -0
  51. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
  52. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test__cron.py +0 -0
  53. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test__regex.py +0 -0
  54. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_event.py +0 -0
  55. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_job.py +0 -0
  56. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_job_exec.py +0 -0
  57. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_job_exec_strategy.py +0 -0
  58. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_logs_audit.py +0 -0
  59. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_logs_trace.py +0 -0
  60. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_params.py +0 -0
  61. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_release.py +0 -0
  62. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_release_queue.py +0 -0
  63. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_result.py +0 -0
  64. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_reusables_template.py +0 -0
  65. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_reusables_template_filter.py +0 -0
  66. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_stage.py +0 -0
  67. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_strategy.py +0 -0
  68. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_utils.py +0 -0
  69. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_workflow.py +0 -0
  70. {ddeutil_workflow-0.0.62 → ddeutil_workflow-0.0.64}/tests/test_workflow_exec_job.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.62
3
+ Version: 0.0.64
4
4
  Summary: Lightweight workflow orchestration
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
@@ -25,6 +25,7 @@ License-File: LICENSE
25
25
  Requires-Dist: ddeutil[checksum]>=0.4.8
26
26
  Requires-Dist: ddeutil-io[toml,yaml]>=0.2.13
27
27
  Requires-Dist: pydantic==2.11.4
28
+ Requires-Dist: pydantic-extra-types==2.10.4
28
29
  Requires-Dist: python-dotenv==1.1.0
29
30
  Requires-Dist: schedule<2.0.0,==1.2.2
30
31
  Provides-Extra: all
@@ -215,19 +216,23 @@ registry-caller/
215
216
  This function will store as module that will import from `WORKFLOW_CORE_REGISTRY_CALLER`
216
217
  value (This config can override by extra parameters with `registry_caller` key).
217
218
 
219
+ > [!NOTE]
220
+ > You can use Pydantic Model as argument of your caller function. The core workflow
221
+ > engine will auto use the `model_validate` method before run your caller function.
222
+
218
223
  ```python
219
- from ddeutil.workflow import Result, tag
224
+ from ddeutil.workflow import Result, CallerSecret, tag
220
225
  from ddeutil.workflow.exceptions import StageException
221
- from pydantic import BaseModel, SecretStr
226
+ from pydantic import BaseModel
222
227
 
223
228
  class AwsCredential(BaseModel):
224
229
  path: str
225
230
  access_client_id: str
226
- access_client_secret: SecretStr
231
+ access_client_secret: CallerSecret
227
232
 
228
233
  class RestAuth(BaseModel):
229
234
  type: str
230
- keys: SecretStr
235
+ keys: CallerSecret
231
236
 
232
237
  @tag("requests", alias="get-api-with-oauth-to-s3")
233
238
  def get_api_with_oauth_to_s3(
@@ -243,6 +248,7 @@ def get_api_with_oauth_to_s3(
243
248
  result.trace.info(f"... {method}: {url}")
244
249
  if method != "post":
245
250
  raise StageException(f"RestAPI does not support for {method} action.")
251
+ # NOTE: If you want to use secret, you can use `auth.keys.get_secret_value()`.
246
252
  return {"records": 1000}
247
253
  ```
248
254
 
@@ -259,45 +265,6 @@ result: Result = workflow.execute(
259
265
  )
260
266
  ```
261
267
 
262
- > [!NOTE]
263
- > So, this package provide the `Schedule` template for this action, and you can
264
- > pass the parameters dynamically for changing align with that running time by
265
- > the `release` prefix.
266
- >
267
- > ```yaml
268
- > schedule-run-local-wf:
269
- >
270
- > # Validate model that use to parsing exists for template file
271
- > type: Schedule
272
- > workflows:
273
- >
274
- > # Map existing workflow that want to deploy with scheduler application.
275
- > # It allows you to pass release parameter that dynamic change depend on the
276
- > # current context of this scheduler application releasing that time.
277
- > - name: run-py-local
278
- > params:
279
- > source-extract: "USD-THB"
280
- > run-date: "${{ release.logical_date }}"
281
- > ```
282
- >
283
- > The main method of the `Schedule` model that use to running is `pending`. If you
284
- > do not pass the `stop` date on this method, it will use config with
285
- > `WORKFLOW_APP_STOP_BOUNDARY_DELTA` key for generate this stop date.
286
- >
287
- > ```python
288
- > from ddeutil.workflow import Schedule
289
- >
290
- > (
291
- > Schedule
292
- > .from_conf("schedule-run-local-wf")
293
- > .pending(stop=None)
294
- > )
295
- > ```
296
-
297
- > [!WARNING]
298
- > The scheduler feature is the expensive feature of this project. You should
299
- > avoid to use it and find a scheduler tool instead.
300
-
301
268
  ## :cookie: Configuration
302
269
 
303
270
  The main configuration that use to dynamic changing this workflow engine for your
@@ -327,19 +294,6 @@ it will use default value and do not raise any error to you.
327
294
  | **TRACE_ENABLE_WRITE** | Log | `false` | |
328
295
  | **AUDIT_PATH** | Log | `./audits` | |
329
296
  | **AUDIT_ENABLE_WRITE** | Log | `true` | A flag that enable logging object saving log to its destination. |
330
- | **MAX_PROCESS** | App | `2` | The maximum process worker number that run in scheduler app module. |
331
- | **MAX_SCHEDULE_PER_PROCESS** | App | `100` | A schedule per process that run parallel. |
332
- | **STOP_BOUNDARY_DELTA** | App | `'{"minutes": 5, "seconds": 20}'` | A time delta value that use to stop scheduler app in json string format. |
333
-
334
- **API Application**:
335
-
336
- This config part use for the workflow application that build from the FastAPI
337
- only.
338
-
339
- | Environment | Component | Default | Description |
340
- |:---------------------------|:-----------:|---------|------------------------------------------------------------------------------------|
341
- | **ENABLE_ROUTE_WORKFLOW** | API | `true` | A flag that enable workflow route to manage execute manually and workflow logging. |
342
- | **ENABLE_ROUTE_SCHEDULE** | API | `true` | A flag that enable run scheduler. |
343
297
 
344
298
  ## :rocket: Deployment
345
299
 
@@ -165,19 +165,23 @@ registry-caller/
165
165
  This function will store as module that will import from `WORKFLOW_CORE_REGISTRY_CALLER`
166
166
  value (This config can override by extra parameters with `registry_caller` key).
167
167
 
168
+ > [!NOTE]
169
+ > You can use Pydantic Model as argument of your caller function. The core workflow
170
+ > engine will auto use the `model_validate` method before run your caller function.
171
+
168
172
  ```python
169
- from ddeutil.workflow import Result, tag
173
+ from ddeutil.workflow import Result, CallerSecret, tag
170
174
  from ddeutil.workflow.exceptions import StageException
171
- from pydantic import BaseModel, SecretStr
175
+ from pydantic import BaseModel
172
176
 
173
177
  class AwsCredential(BaseModel):
174
178
  path: str
175
179
  access_client_id: str
176
- access_client_secret: SecretStr
180
+ access_client_secret: CallerSecret
177
181
 
178
182
  class RestAuth(BaseModel):
179
183
  type: str
180
- keys: SecretStr
184
+ keys: CallerSecret
181
185
 
182
186
  @tag("requests", alias="get-api-with-oauth-to-s3")
183
187
  def get_api_with_oauth_to_s3(
@@ -193,6 +197,7 @@ def get_api_with_oauth_to_s3(
193
197
  result.trace.info(f"... {method}: {url}")
194
198
  if method != "post":
195
199
  raise StageException(f"RestAPI does not support for {method} action.")
200
+ # NOTE: If you want to use secret, you can use `auth.keys.get_secret_value()`.
196
201
  return {"records": 1000}
197
202
  ```
198
203
 
@@ -209,45 +214,6 @@ result: Result = workflow.execute(
209
214
  )
210
215
  ```
211
216
 
212
- > [!NOTE]
213
- > So, this package provide the `Schedule` template for this action, and you can
214
- > pass the parameters dynamically for changing align with that running time by
215
- > the `release` prefix.
216
- >
217
- > ```yaml
218
- > schedule-run-local-wf:
219
- >
220
- > # Validate model that use to parsing exists for template file
221
- > type: Schedule
222
- > workflows:
223
- >
224
- > # Map existing workflow that want to deploy with scheduler application.
225
- > # It allows you to pass release parameter that dynamic change depend on the
226
- > # current context of this scheduler application releasing that time.
227
- > - name: run-py-local
228
- > params:
229
- > source-extract: "USD-THB"
230
- > run-date: "${{ release.logical_date }}"
231
- > ```
232
- >
233
- > The main method of the `Schedule` model that use to running is `pending`. If you
234
- > do not pass the `stop` date on this method, it will use config with
235
- > `WORKFLOW_APP_STOP_BOUNDARY_DELTA` key for generate this stop date.
236
- >
237
- > ```python
238
- > from ddeutil.workflow import Schedule
239
- >
240
- > (
241
- > Schedule
242
- > .from_conf("schedule-run-local-wf")
243
- > .pending(stop=None)
244
- > )
245
- > ```
246
-
247
- > [!WARNING]
248
- > The scheduler feature is the expensive feature of this project. You should
249
- > avoid to use it and find a scheduler tool instead.
250
-
251
217
  ## :cookie: Configuration
252
218
 
253
219
  The main configuration that use to dynamic changing this workflow engine for your
@@ -277,19 +243,6 @@ it will use default value and do not raise any error to you.
277
243
  | **TRACE_ENABLE_WRITE** | Log | `false` | |
278
244
  | **AUDIT_PATH** | Log | `./audits` | |
279
245
  | **AUDIT_ENABLE_WRITE** | Log | `true` | A flag that enable logging object saving log to its destination. |
280
- | **MAX_PROCESS** | App | `2` | The maximum process worker number that run in scheduler app module. |
281
- | **MAX_SCHEDULE_PER_PROCESS** | App | `100` | A schedule per process that run parallel. |
282
- | **STOP_BOUNDARY_DELTA** | App | `'{"minutes": 5, "seconds": 20}'` | A time delta value that use to stop scheduler app in json string format. |
283
-
284
- **API Application**:
285
-
286
- This config part use for the workflow application that build from the FastAPI
287
- only.
288
-
289
- | Environment | Component | Default | Description |
290
- |:---------------------------|:-----------:|---------|------------------------------------------------------------------------------------|
291
- | **ENABLE_ROUTE_WORKFLOW** | API | `true` | A flag that enable workflow route to manage execute manually and workflow logging. |
292
- | **ENABLE_ROUTE_SCHEDULE** | API | `true` | A flag that enable run scheduler. |
293
246
 
294
247
  ## :rocket: Deployment
295
248
 
@@ -28,6 +28,7 @@ dependencies = [
28
28
  "ddeutil[checksum]>=0.4.8",
29
29
  "ddeutil-io[yaml,toml]>=0.2.13",
30
30
  "pydantic==2.11.4",
31
+ "pydantic-extra-types==2.10.4",
31
32
  "python-dotenv==1.1.0",
32
33
  "schedule==1.2.2,<2.0.0",
33
34
  ]
@@ -0,0 +1 @@
1
+ __version__: str = "0.0.64"
@@ -5,12 +5,7 @@
5
5
  # ------------------------------------------------------------------------------
6
6
  from .__cron import CronJob, CronRunner
7
7
  from .__types import DictData, DictStr, Matrix, Re, TupleStr
8
- from .conf import (
9
- Config,
10
- FileLoad,
11
- config,
12
- env,
13
- )
8
+ from .conf import *
14
9
  from .event import *
15
10
  from .exceptions import *
16
11
  from .job import *
@@ -37,31 +32,7 @@ from .result import (
37
32
  Result,
38
33
  Status,
39
34
  )
40
- from .reusables import (
41
- FILTERS,
42
- FilterFunc,
43
- FilterRegistry,
44
- ReturnTagFunc,
45
- TagFunc,
46
- custom_filter,
47
- extract_call,
48
- get_args_const,
49
- has_template,
50
- make_filter_registry,
51
- make_registry,
52
- map_post_filter,
53
- not_in_template,
54
- param2template,
55
- str2template,
56
- tag,
57
- )
58
- from .scheduler import (
59
- Schedule,
60
- ScheduleWorkflow,
61
- schedule_control,
62
- schedule_runner,
63
- schedule_task,
64
- )
35
+ from .reusables import *
65
36
  from .stages import *
66
37
  from .utils import *
67
38
  from .workflow import *
@@ -0,0 +1,91 @@
1
+ # ------------------------------------------------------------------------------
2
+ # Copyright (c) 2022 Korawich Anuttra. All rights reserved.
3
+ # Licensed under the MIT License. See LICENSE in the project root for
4
+ # license information.
5
+ # ------------------------------------------------------------------------------
6
+ from __future__ import annotations
7
+
8
+ import contextlib
9
+ from collections.abc import AsyncIterator
10
+
11
+ from dotenv import load_dotenv
12
+ from fastapi import FastAPI, Request
13
+ from fastapi import status as st
14
+ from fastapi.encoders import jsonable_encoder
15
+ from fastapi.exceptions import RequestValidationError
16
+ from fastapi.middleware.cors import CORSMiddleware
17
+ from fastapi.middleware.gzip import GZipMiddleware
18
+ from fastapi.responses import UJSONResponse
19
+
20
+ from ..__about__ import __version__
21
+ from ..conf import api_config
22
+ from ..logs import get_logger
23
+ from .routes import job, log, workflow
24
+
25
+ load_dotenv()
26
+ logger = get_logger("uvicorn.error")
27
+
28
+
29
+ @contextlib.asynccontextmanager
30
+ async def lifespan(_: FastAPI) -> AsyncIterator[dict[str, list]]:
31
+ """Lifespan function for the FastAPI application."""
32
+ yield {}
33
+
34
+
35
+ app = FastAPI(
36
+ titile="Workflow",
37
+ description=(
38
+ "This is a workflow FastAPI application that use to manage manual "
39
+ "execute, logging, and schedule workflow via RestAPI."
40
+ ),
41
+ version=__version__,
42
+ lifespan=lifespan,
43
+ default_response_class=UJSONResponse,
44
+ )
45
+ app.add_middleware(GZipMiddleware, minimum_size=1000)
46
+ origins: list[str] = [
47
+ "http://localhost",
48
+ "http://localhost:88",
49
+ "http://localhost:80",
50
+ ]
51
+ app.add_middleware(
52
+ CORSMiddleware,
53
+ allow_origins=origins,
54
+ allow_credentials=True,
55
+ allow_methods=["*"],
56
+ allow_headers=["*"],
57
+ )
58
+
59
+
60
+ @app.get(path="/", response_class=UJSONResponse)
61
+ async def health():
62
+ """Index view that not return any template without json status."""
63
+ return {"message": "Workflow already start up with healthy status."}
64
+
65
+
66
+ # NOTE Add the jobs and logs routes by default.
67
+ app.include_router(job, prefix=api_config.prefix_path)
68
+ app.include_router(log, prefix=api_config.prefix_path)
69
+ app.include_router(workflow, prefix=api_config.prefix_path)
70
+
71
+
72
+ @app.exception_handler(RequestValidationError)
73
+ async def validation_exception_handler(
74
+ request: Request, exc: RequestValidationError
75
+ ):
76
+ _ = request
77
+ return UJSONResponse(
78
+ status_code=st.HTTP_422_UNPROCESSABLE_ENTITY,
79
+ content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}),
80
+ )
81
+
82
+
83
+ if __name__ == "__main__":
84
+ import uvicorn
85
+
86
+ uvicorn.run(
87
+ app,
88
+ host="0.0.0.0",
89
+ port=80,
90
+ log_level="DEBUG",
91
+ )
@@ -5,5 +5,4 @@
5
5
  # ------------------------------------------------------------------------------
6
6
  from .job import job_route as job
7
7
  from .logs import log_route as log
8
- from .schedules import schedule_route as schedule
9
8
  from .workflows import workflow_route as workflow
@@ -69,7 +69,6 @@ async def job_execute(
69
69
  by_alias=True,
70
70
  exclude_none=False,
71
71
  exclude_unset=True,
72
- exclude_defaults=True,
73
72
  ),
74
73
  "params": params,
75
74
  "context": context,
@@ -44,7 +44,6 @@ async def get_traces(
44
44
  by_alias=True,
45
45
  exclude_none=True,
46
46
  exclude_unset=True,
47
- exclude_defaults=True,
48
47
  )
49
48
  for trace in result.trace.find_traces()
50
49
  ],
@@ -73,7 +72,6 @@ async def get_trace_with_id(run_id: str):
73
72
  by_alias=True,
74
73
  exclude_none=True,
75
74
  exclude_unset=True,
76
- exclude_defaults=True,
77
75
  )
78
76
  ),
79
77
  }
@@ -56,7 +56,6 @@ async def get_workflow_by_name(name: str) -> DictData:
56
56
  by_alias=True,
57
57
  exclude_none=False,
58
58
  exclude_unset=True,
59
- exclude_defaults=True,
60
59
  )
61
60
 
62
61
 
@@ -99,7 +98,6 @@ async def get_workflow_audits(name: str):
99
98
  by_alias=True,
100
99
  exclude_none=False,
101
100
  exclude_unset=True,
102
- exclude_defaults=True,
103
101
  )
104
102
  for audit in get_audit().find_audits(name=name)
105
103
  ],
@@ -133,6 +131,5 @@ async def get_workflow_release_audit(name: str, release: str):
133
131
  by_alias=True,
134
132
  exclude_none=False,
135
133
  exclude_unset=True,
136
- exclude_defaults=True,
137
134
  ),
138
135
  }
@@ -6,11 +6,9 @@
6
6
  from __future__ import annotations
7
7
 
8
8
  import copy
9
- import json
10
9
  import os
11
10
  from abc import ABC, abstractmethod
12
11
  from collections.abc import Iterator
13
- from datetime import timedelta
14
12
  from functools import cached_property
15
13
  from inspect import isclass
16
14
  from pathlib import Path
@@ -18,8 +16,9 @@ from typing import Final, Optional, Protocol, TypeVar, Union
18
16
  from zoneinfo import ZoneInfo
19
17
 
20
18
  from ddeutil.core import str2bool
21
- from ddeutil.io import YamlFlResolve
19
+ from ddeutil.io import YamlFlResolve, search_env_replace
22
20
  from ddeutil.io.paths import glob_files, is_ignored, read_ignore
21
+ from pydantic import SecretStr, TypeAdapter
23
22
 
24
23
  from .__types import DictData
25
24
 
@@ -162,28 +161,6 @@ class Config: # pragma: no cov
162
161
  def max_queue_complete_hist(self) -> int:
163
162
  return int(env("CORE_MAX_QUEUE_COMPLETE_HIST", "16"))
164
163
 
165
- # NOTE: App
166
- @property
167
- def max_schedule_process(self) -> int:
168
- return int(env("APP_MAX_PROCESS", "2"))
169
-
170
- @property
171
- def max_schedule_per_process(self) -> int:
172
- return int(env("APP_MAX_SCHEDULE_PER_PROCESS", "100"))
173
-
174
- @property
175
- def stop_boundary_delta(self) -> timedelta:
176
- stop_boundary_delta_str: str = env(
177
- "APP_STOP_BOUNDARY_DELTA", '{"minutes": 5, "seconds": 20}'
178
- )
179
- try:
180
- return timedelta(**json.loads(stop_boundary_delta_str))
181
- except Exception as err:
182
- raise ValueError(
183
- "Config `WORKFLOW_APP_STOP_BOUNDARY_DELTA` can not parsing to"
184
- f"timedelta with {stop_boundary_delta_str}."
185
- ) from err
186
-
187
164
 
188
165
  class APIConfig:
189
166
  """API Config object."""
@@ -192,14 +169,6 @@ class APIConfig:
192
169
  def prefix_path(self) -> str:
193
170
  return env("API_PREFIX_PATH", "/api/v1")
194
171
 
195
- @property
196
- def enable_route_workflow(self) -> bool:
197
- return str2bool(env("API_ENABLE_ROUTE_WORKFLOW", "true"))
198
-
199
- @property
200
- def enable_route_schedule(self) -> bool:
201
- return str2bool(env("API_ENABLE_ROUTE_SCHEDULE", "true"))
202
-
203
172
 
204
173
  class BaseLoad(ABC): # pragma: no cov
205
174
  """Base Load object is the abstraction object for any Load object that
@@ -321,15 +290,16 @@ class FileLoad(BaseLoad):
321
290
  *,
322
291
  path: Optional[Path] = None,
323
292
  paths: Optional[list[Path]] = None,
324
- excluded: list[str] | None = None,
293
+ excluded: Optional[list[str]] = None,
325
294
  extras: Optional[DictData] = None,
326
295
  ) -> Iterator[tuple[str, DictData]]:
327
296
  """Find all data that match with object type in config path. This class
328
297
  method can use include and exclude list of identity name for filter and
329
298
  adds-on.
330
299
 
331
- :param obj: An object that want to validate matching before return.
332
- :param path: A config path object.
300
+ :param obj: (object) An object that want to validate matching before
301
+ return.
302
+ :param path: (Path) A config path object.
333
303
  :param paths: (list[Path]) A list of config path object.
334
304
  :param excluded: An included list of data key that want to filter from
335
305
  data.
@@ -474,3 +444,37 @@ class Loader(Protocol): # pragma: no cov
474
444
  def finds(
475
445
  cls, obj: object, *args, **kwargs
476
446
  ) -> Iterator[tuple[str, DictData]]: ...
447
+
448
+
449
+ def pass_env(value: T) -> T: # pragma: no cov
450
+ """Passing environment variable to an input value.
451
+
452
+ :param value: (Any) A value that want to pass env var searching.
453
+
454
+ :rtype: Any
455
+ """
456
+ if isinstance(value, dict):
457
+ return {k: pass_env(value[k]) for k in value}
458
+ elif isinstance(value, (list, tuple, set)):
459
+ return type(value)([pass_env(i) for i in value])
460
+ if not isinstance(value, str):
461
+ return value
462
+
463
+ rs: str = search_env_replace(value)
464
+ return None if rs == "null" else rs
465
+
466
+
467
+ class CallerSecret(SecretStr): # pragma: no cov
468
+ """Workflow Secret String model."""
469
+
470
+ def get_secret_value(self) -> str:
471
+ """Override get_secret_value by adding pass_env before return the
472
+ real-value.
473
+
474
+ :rtype: str
475
+ """
476
+ return pass_env(super().get_secret_value())
477
+
478
+
479
+ # NOTE: Define the caller secret type for use it directly in the caller func.
480
+ CallerSecretType = TypeAdapter(CallerSecret)
@@ -17,6 +17,7 @@ from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
17
17
  from pydantic import BaseModel, ConfigDict, Field, ValidationInfo
18
18
  from pydantic.functional_serializers import field_serializer
19
19
  from pydantic.functional_validators import field_validator, model_validator
20
+ from pydantic_extra_types.timezone_name import TimeZoneName
20
21
  from typing_extensions import Self
21
22
 
22
23
  from .__cron import WEEKDAYS, CronJob, CronJobYear, CronRunner, Options
@@ -92,7 +93,7 @@ class Crontab(BaseModel):
92
93
  ),
93
94
  ]
94
95
  tz: Annotated[
95
- str,
96
+ TimeZoneName,
96
97
  Field(
97
98
  description="A timezone string value",
98
99
  alias="timezone",
@@ -83,6 +83,3 @@ class WorkflowException(BaseWorkflowException): ...
83
83
 
84
84
 
85
85
  class ParamValueException(WorkflowException): ...
86
-
87
-
88
- class ScheduleException(BaseWorkflowException): ...