ddeutil-workflow 0.0.66__tar.gz → 0.0.68__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 (58) hide show
  1. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/PKG-INFO +14 -36
  2. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/README.md +9 -22
  3. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/pyproject.toml +8 -22
  4. ddeutil_workflow-0.0.68/src/ddeutil/workflow/__about__.py +1 -0
  5. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/api/__init__.py +20 -19
  6. ddeutil_workflow-0.0.66/src/ddeutil/workflow/api/logs.py → ddeutil_workflow-0.0.68/src/ddeutil/workflow/api/log_conf.py +28 -15
  7. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/api/routes/__init__.py +3 -3
  8. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/api/routes/job.py +42 -16
  9. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/api/routes/logs.py +7 -7
  10. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/api/routes/workflows.py +10 -9
  11. ddeutil_workflow-0.0.68/src/ddeutil/workflow/cli.py +119 -0
  12. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/conf.py +7 -3
  13. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/errors.py +0 -3
  14. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/event.py +4 -3
  15. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/logs.py +18 -14
  16. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/result.py +12 -7
  17. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/stages.py +149 -35
  18. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/utils.py +1 -52
  19. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil_workflow.egg-info/PKG-INFO +14 -36
  20. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil_workflow.egg-info/SOURCES.txt +1 -1
  21. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil_workflow.egg-info/requires.txt +3 -15
  22. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_result.py +4 -1
  23. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_utils.py +0 -10
  24. ddeutil_workflow-0.0.66/src/ddeutil/workflow/__about__.py +0 -1
  25. ddeutil_workflow-0.0.66/src/ddeutil/workflow/cli.py +0 -66
  26. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/LICENSE +0 -0
  27. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/setup.cfg +0 -0
  28. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/__cron.py +0 -0
  29. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/__init__.py +0 -0
  30. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/__main__.py +0 -0
  31. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/__types.py +0 -0
  32. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/job.py +0 -0
  33. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/params.py +0 -0
  34. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/reusables.py +0 -0
  35. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil/workflow/workflow.py +0 -0
  36. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil_workflow.egg-info/dependency_links.txt +0 -0
  37. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil_workflow.egg-info/entry_points.txt +0 -0
  38. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/src/ddeutil_workflow.egg-info/top_level.txt +0 -0
  39. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test__cron.py +0 -0
  40. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test__regex.py +0 -0
  41. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_conf.py +0 -0
  42. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_errors.py +0 -0
  43. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_event.py +0 -0
  44. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_job.py +0 -0
  45. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_job_exec.py +0 -0
  46. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_job_exec_strategy.py +0 -0
  47. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_logs_audit.py +0 -0
  48. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_logs_trace.py +0 -0
  49. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_params.py +0 -0
  50. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_reusables_call_tag.py +0 -0
  51. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_reusables_func_model.py +0 -0
  52. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_reusables_template.py +0 -0
  53. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_reusables_template_filter.py +0 -0
  54. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_strategy.py +0 -0
  55. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_workflow.py +0 -0
  56. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_workflow_exec.py +0 -0
  57. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_workflow_exec_job.py +0 -0
  58. {ddeutil_workflow-0.0.66 → ddeutil_workflow-0.0.68}/tests/test_workflow_release.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.66
4
- Summary: Lightweight workflow orchestration
3
+ Version: 0.0.68
4
+ Summary: Lightweight workflow orchestration with YAML template
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
7
7
  Project-URL: Homepage, https://github.com/ddeutils/ddeutil-workflow/
@@ -24,10 +24,10 @@ Description-Content-Type: text/markdown
24
24
  License-File: LICENSE
25
25
  Requires-Dist: ddeutil[checksum]>=0.4.8
26
26
  Requires-Dist: ddeutil-io[toml,yaml]>=0.2.14
27
- Requires-Dist: pydantic==2.11.4
27
+ Requires-Dist: pydantic==2.11.5
28
28
  Requires-Dist: pydantic-extra-types==2.10.4
29
29
  Requires-Dist: python-dotenv==1.1.0
30
- Requires-Dist: typer==0.15.4
30
+ Requires-Dist: typer>=0.16.0
31
31
  Provides-Extra: all
32
32
  Requires-Dist: fastapi<1.0.0,>=0.115.0; extra == "all"
33
33
  Requires-Dist: uvicorn; extra == "all"
@@ -35,18 +35,9 @@ Requires-Dist: httpx; extra == "all"
35
35
  Requires-Dist: ujson; extra == "all"
36
36
  Requires-Dist: aiofiles; extra == "all"
37
37
  Requires-Dist: aiohttp; extra == "all"
38
- Provides-Extra: api
39
- Requires-Dist: fastapi<1.0.0,>=0.115.0; extra == "api"
40
- Requires-Dist: uvicorn; extra == "api"
41
- Requires-Dist: httpx; extra == "api"
42
- Requires-Dist: ujson; extra == "api"
43
- Provides-Extra: async
44
- Requires-Dist: aiofiles; extra == "async"
45
- Requires-Dist: aiohttp; extra == "async"
38
+ Requires-Dist: requests==2.32.3; extra == "all"
46
39
  Provides-Extra: docker
47
40
  Requires-Dist: docker==7.1.0; extra == "docker"
48
- Provides-Extra: self-hosted
49
- Requires-Dist: requests==2.32.3; extra == "self-hosted"
50
41
  Dynamic: license-file
51
42
 
52
43
  # Workflow Orchestration
@@ -142,10 +133,10 @@ the base deps.
142
133
  If you want to install this package with application add-ons, you should add
143
134
  `app` in installation;
144
135
 
145
- | Use-case | Install Optional | Support |
146
- |----------------|--------------------------|:-------------------:|
147
- | Python | `ddeutil-workflow` | :heavy_check_mark: |
148
- | FastAPI Server | `ddeutil-workflow[api]` | :heavy_check_mark: |
136
+ | Use-case | Install Optional | Support |
137
+ |----------------|-------------------------|:-------:|
138
+ | Python | `ddeutil-workflow` ||
139
+ | FastAPI Server | `ddeutil-workflow[all]` | ✅ |
149
140
 
150
141
  ## 🎯 Usage
151
142
 
@@ -300,40 +291,27 @@ it will use default value and do not raise any error to you.
300
291
  ## :rocket: Deployment
301
292
 
302
293
  This package able to run as an application service for receive manual trigger
303
- from any node via RestAPI or use to be Scheduler background application
304
- like crontab job but via Python API or FastAPI app.
294
+ from any node via RestAPI with the FastAPI package.
305
295
 
306
296
  ### API Server
307
297
 
308
298
  This server use FastAPI package to be the base application.
309
299
 
310
300
  ```shell
311
- (.venv) $ uvicorn ddeutil.workflow.api:app \
312
- --host 127.0.0.1 \
313
- --port 80 \
314
- --no-access-log
301
+ (.venv) $ workflow-cli api --host 127.0.0.1 --port 80
315
302
  ```
316
303
 
317
304
  > [!NOTE]
318
305
  > If this package already deploy, it is able to use multiprocess;
319
- > `uvicorn ddeutil.workflow.api:app --host 127.0.0.1 --port 80 --workers 4`
320
-
321
- ### Local Schedule
322
-
323
- > [!WARNING]
324
- > This CLI does not implement yet.
325
-
326
- ```shell
327
- (.venv) $ ddeutil-workflow schedule
328
- ```
306
+ > `$ workflow-cli api --host 127.0.0.1 --port 80 --workers 4`
329
307
 
330
308
  ### Docker Container
331
309
 
332
310
  Build a Docker container from this package.
333
311
 
334
312
  ```shell
335
- $ docker build -t ddeutil-workflow:latest -f .container/Dockerfile .
336
- $ docker run -i ddeutil-workflow:latest ddeutil-workflow
313
+ $ docker pull ghcr.io/ddeutils/ddeutil-workflow:latest
314
+ $ docker run --rm ghcr.io/ddeutils/ddeutil-workflow:latest ddeutil-worker
337
315
  ```
338
316
 
339
317
  ## :speech_balloon: Contribute
@@ -91,10 +91,10 @@ the base deps.
91
91
  If you want to install this package with application add-ons, you should add
92
92
  `app` in installation;
93
93
 
94
- | Use-case | Install Optional | Support |
95
- |----------------|--------------------------|:-------------------:|
96
- | Python | `ddeutil-workflow` | :heavy_check_mark: |
97
- | FastAPI Server | `ddeutil-workflow[api]` | :heavy_check_mark: |
94
+ | Use-case | Install Optional | Support |
95
+ |----------------|-------------------------|:-------:|
96
+ | Python | `ddeutil-workflow` ||
97
+ | FastAPI Server | `ddeutil-workflow[all]` | ✅ |
98
98
 
99
99
  ## 🎯 Usage
100
100
 
@@ -249,40 +249,27 @@ it will use default value and do not raise any error to you.
249
249
  ## :rocket: Deployment
250
250
 
251
251
  This package able to run as an application service for receive manual trigger
252
- from any node via RestAPI or use to be Scheduler background application
253
- like crontab job but via Python API or FastAPI app.
252
+ from any node via RestAPI with the FastAPI package.
254
253
 
255
254
  ### API Server
256
255
 
257
256
  This server use FastAPI package to be the base application.
258
257
 
259
258
  ```shell
260
- (.venv) $ uvicorn ddeutil.workflow.api:app \
261
- --host 127.0.0.1 \
262
- --port 80 \
263
- --no-access-log
259
+ (.venv) $ workflow-cli api --host 127.0.0.1 --port 80
264
260
  ```
265
261
 
266
262
  > [!NOTE]
267
263
  > If this package already deploy, it is able to use multiprocess;
268
- > `uvicorn ddeutil.workflow.api:app --host 127.0.0.1 --port 80 --workers 4`
269
-
270
- ### Local Schedule
271
-
272
- > [!WARNING]
273
- > This CLI does not implement yet.
274
-
275
- ```shell
276
- (.venv) $ ddeutil-workflow schedule
277
- ```
264
+ > `$ workflow-cli api --host 127.0.0.1 --port 80 --workers 4`
278
265
 
279
266
  ### Docker Container
280
267
 
281
268
  Build a Docker container from this package.
282
269
 
283
270
  ```shell
284
- $ docker build -t ddeutil-workflow:latest -f .container/Dockerfile .
285
- $ docker run -i ddeutil-workflow:latest ddeutil-workflow
271
+ $ docker pull ghcr.io/ddeutils/ddeutil-workflow:latest
272
+ $ docker run --rm ghcr.io/ddeutils/ddeutil-workflow:latest ddeutil-worker
286
273
  ```
287
274
 
288
275
  ## :speech_balloon: Contribute
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ddeutil-workflow"
7
- description = "Lightweight workflow orchestration"
7
+ description = "Lightweight workflow orchestration with YAML template"
8
8
  readme = {file = "README.md", content-type = "text/markdown"}
9
9
  license = {text = "MIT"}
10
10
  authors = [{ name = "ddeutils", email = "korawich.anu@gmail.com" }]
@@ -27,11 +27,10 @@ requires-python = ">=3.9.13"
27
27
  dependencies = [
28
28
  "ddeutil[checksum]>=0.4.8",
29
29
  "ddeutil-io[yaml,toml]>=0.2.14",
30
- "pydantic==2.11.4",
30
+ "pydantic==2.11.5",
31
31
  "pydantic-extra-types==2.10.4",
32
32
  "python-dotenv==1.1.0",
33
- # "schedule==1.2.2,<2.0.0",
34
- "typer==0.15.4",
33
+ "typer>=0.16.0",
35
34
  ]
36
35
  dynamic = ["version"]
37
36
 
@@ -43,23 +42,11 @@ all = [
43
42
  "ujson",
44
43
  "aiofiles",
45
44
  "aiohttp",
46
- ]
47
- api = [
48
- "fastapi>=0.115.0,<1.0.0",
49
- "uvicorn",
50
- "httpx",
51
- "ujson",
52
- ]
53
- async = [
54
- "aiofiles",
55
- "aiohttp",
45
+ "requests==2.32.3",
56
46
  ]
57
47
  docker = [
58
48
  "docker==7.1.0",
59
49
  ]
60
- self_hosted = [
61
- "requests==2.32.3",
62
- ]
63
50
 
64
51
  [project.urls]
65
52
  Homepage = "https://github.com/ddeutils/ddeutil-workflow/"
@@ -91,9 +78,10 @@ omit = [
91
78
  "src/ddeutil/workflow/__about__.py",
92
79
  "src/ddeutil/workflow/__cron.py",
93
80
  "src/ddeutil/workflow/__main__.py",
81
+ "src/ddeutil/workflow/__types.py",
94
82
  "src/ddeutil/workflow/cli.py",
95
83
  "src/ddeutil/workflow/api/__init__.py",
96
- "src/ddeutil/workflow/api/logs.py",
84
+ "src/ddeutil/workflow/api/log_conf.py",
97
85
  "src/ddeutil/workflow/api/routes/__init__.py",
98
86
  "src/ddeutil/workflow/api/routes/job.py",
99
87
  "src/ddeutil/workflow/api/routes/logs.py",
@@ -109,12 +97,10 @@ exclude_lines = [
109
97
  [tool.pytest.ini_options]
110
98
  pythonpath = ["src"]
111
99
  asyncio_default_fixture_loop_scope = "fuction"
112
- # NOTE: You can deslect multiple markers by '-m "not (poke or api)"'
100
+ # NOTE: You can deslect multiple markers by '-m "not (asyncio or api)"'
113
101
  markers = [
114
- "poke: marks tests as slow by poking (deselect with '-m \"not poke\"')",
115
- "schedule: marks tests as schedule (deselect with '-m \"not schedule\"')",
116
102
  "api: marks tests as api (deselect with '-m \"not api\"')",
117
- "asyncio: marks async testcases",
103
+ "asyncio: marks async test cases",
118
104
  ]
119
105
  console_output_style = "count"
120
106
  addopts = [
@@ -0,0 +1 @@
1
+ __version__: str = "0.0.68"
@@ -6,6 +6,7 @@
6
6
  from __future__ import annotations
7
7
 
8
8
  import contextlib
9
+ import logging
9
10
  from collections.abc import AsyncIterator
10
11
 
11
12
  from dotenv import load_dotenv
@@ -19,11 +20,10 @@ from fastapi.responses import UJSONResponse
19
20
 
20
21
  from ..__about__ import __version__
21
22
  from ..conf import api_config
22
- from ..logs import get_logger
23
23
  from .routes import job, log, workflow
24
24
 
25
25
  load_dotenv()
26
- logger = get_logger("uvicorn.error")
26
+ logger = logging.getLogger("uvicorn.error")
27
27
 
28
28
 
29
29
  @contextlib.asynccontextmanager
@@ -58,12 +58,16 @@ app.add_middleware(
58
58
 
59
59
 
60
60
  @app.get(path="/", response_class=UJSONResponse)
61
- async def health():
61
+ async def health() -> UJSONResponse:
62
62
  """Index view that not return any template without json status."""
63
- return {"message": "Workflow already start up with healthy status."}
63
+ logger.info("[API]: Workflow API Application already running ...")
64
+ return UJSONResponse(
65
+ content={"message": "Workflow already start up with healthy status."},
66
+ status_code=st.HTTP_200_OK,
67
+ )
64
68
 
65
69
 
66
- # NOTE Add the jobs and logs routes by default.
70
+ # NOTE: Add the jobs and logs routes by default.
67
71
  app.include_router(job, prefix=api_config.prefix_path)
68
72
  app.include_router(log, prefix=api_config.prefix_path)
69
73
  app.include_router(workflow, prefix=api_config.prefix_path)
@@ -71,21 +75,18 @@ app.include_router(workflow, prefix=api_config.prefix_path)
71
75
 
72
76
  @app.exception_handler(RequestValidationError)
73
77
  async def validation_exception_handler(
74
- request: Request, exc: RequestValidationError
75
- ):
78
+ request: Request,
79
+ exc: RequestValidationError,
80
+ ) -> UJSONResponse:
81
+ """Error Handler for model validate does not valid."""
76
82
  _ = request
77
83
  return UJSONResponse(
78
84
  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",
85
+ content=jsonable_encoder(
86
+ {
87
+ "message": "Body does not parsing with model.",
88
+ "detail": exc.errors(),
89
+ "body": exc.body,
90
+ }
91
+ ),
91
92
  )
@@ -1,6 +1,8 @@
1
+ from typing import Any
2
+
1
3
  from ..conf import config
2
4
 
3
- LOGGING_CONFIG = { # pragma: no cov
5
+ LOGGING_CONFIG: dict[str, Any] = { # pragma: no cov
4
6
  "version": 1,
5
7
  "disable_existing_loggers": False,
6
8
  "formatters": {
@@ -22,38 +24,49 @@ LOGGING_CONFIG = { # pragma: no cov
22
24
  "stream": "ext://sys.stderr",
23
25
  },
24
26
  "stream_handler": {
27
+ # "formatter": "standard",
25
28
  "formatter": "custom_formatter",
26
29
  "class": "logging.StreamHandler",
27
30
  "stream": "ext://sys.stdout",
28
31
  },
29
- "file_handler": {
30
- "formatter": "custom_formatter",
31
- "class": "logging.handlers.RotatingFileHandler",
32
- "filename": "logs/app.log",
33
- "maxBytes": 1024 * 1024 * 1,
34
- "backupCount": 3,
35
- },
32
+ # "file_handler": {
33
+ # "formatter": "custom_formatter",
34
+ # "class": "logging.handlers.RotatingFileHandler",
35
+ # "filename": "logs/app.log",
36
+ # "maxBytes": 1024 * 1024 * 1,
37
+ # "backupCount": 3,
38
+ # },
36
39
  },
37
40
  "loggers": {
38
41
  "uvicorn": {
39
- "handlers": ["default", "file_handler"],
42
+ # "handlers": ["default", "file_handler"],
43
+ "handlers": ["default"],
40
44
  "level": "DEBUG" if config.debug else "INFO",
41
45
  "propagate": False,
42
46
  },
43
47
  "uvicorn.access": {
44
- "handlers": ["stream_handler", "file_handler"],
48
+ # "handlers": ["stream_handler", "file_handler"],
49
+ "handlers": ["stream_handler"],
45
50
  "level": "DEBUG" if config.debug else "INFO",
46
51
  "propagate": False,
47
52
  },
48
53
  "uvicorn.error": {
49
- "handlers": ["stream_handler", "file_handler"],
54
+ # "handlers": ["stream_handler", "file_handler"],
55
+ "handlers": ["stream_handler"],
50
56
  "level": "DEBUG" if config.debug else "INFO",
51
57
  "propagate": False,
52
58
  },
53
- # "uvicorn.asgi": {
54
- # "handlers": ["stream_handler", "file_handler"],
55
- # "level": "TRACE",
56
- # "propagate": False,
59
+ "uvicorn.asgi": {
60
+ # "handlers": ["stream_handler", "file_handler"],
61
+ "handlers": ["stream_handler"],
62
+ "level": "TRACE",
63
+ "propagate": False,
64
+ },
65
+ # "ddeutil.workflow": {
66
+ # "handlers": ["stream_handler"],
67
+ # "level": "INFO",
68
+ # # "propagate": False,
69
+ # "propagate": True,
57
70
  # },
58
71
  },
59
72
  }
@@ -3,6 +3,6 @@
3
3
  # Licensed under the MIT License. See LICENSE in the project root for
4
4
  # license information.
5
5
  # ------------------------------------------------------------------------------
6
- from .job import job_route as job
7
- from .logs import log_route as log
8
- from .workflows import workflow_route as workflow
6
+ from .job import router as job
7
+ from .logs import router as log
8
+ from .workflows import router as workflow
@@ -5,20 +5,21 @@
5
5
  # ------------------------------------------------------------------------------
6
6
  from __future__ import annotations
7
7
 
8
+ import logging
8
9
  from typing import Any, Optional
9
10
 
10
11
  from fastapi import APIRouter
12
+ from fastapi import status as st
11
13
  from fastapi.responses import UJSONResponse
12
14
  from pydantic import BaseModel, Field
13
15
 
14
16
  from ...__types import DictData
15
17
  from ...errors import JobError
16
18
  from ...job import Job
17
- from ...logs import get_logger
18
19
  from ...result import Result
19
20
 
20
- logger = get_logger("uvicorn.error")
21
- job_route = APIRouter(prefix="/job", tags=["job"])
21
+ logger = logging.getLogger("uvicorn.error")
22
+ router = APIRouter(prefix="/job", tags=["job"])
22
23
 
23
24
 
24
25
  class ResultCreate(BaseModel):
@@ -32,14 +33,19 @@ class ResultCreate(BaseModel):
32
33
  )
33
34
 
34
35
 
35
- @job_route.post(path="/execute/", response_class=UJSONResponse)
36
+ @router.post(
37
+ path="/execute/",
38
+ response_class=UJSONResponse,
39
+ status_code=st.HTTP_200_OK,
40
+ )
36
41
  async def job_execute(
37
42
  result: ResultCreate,
38
43
  job: Job,
39
44
  params: dict[str, Any],
40
45
  extras: Optional[dict[str, Any]] = None,
41
- ):
46
+ ) -> UJSONResponse:
42
47
  """Execute job via RestAPI with execute route path."""
48
+ logger.info("[API]: Start execute job ...")
43
49
  rs: Result = Result(
44
50
  run_id=result.run_id,
45
51
  parent_run_id=result.parent_run_id,
@@ -61,15 +67,35 @@ async def job_execute(
61
67
  )
62
68
  except JobError as err:
63
69
  rs.trace.error(f"[JOB]: {err.__class__.__name__}: {err}")
70
+ return UJSONResponse(
71
+ content={
72
+ "message": str(err),
73
+ "result": {
74
+ "run_id": rs.run_id,
75
+ "parent_run_id": rs.parent_run_id,
76
+ },
77
+ "job": job.model_dump(
78
+ by_alias=True,
79
+ exclude_none=False,
80
+ exclude_unset=True,
81
+ ),
82
+ "params": params,
83
+ "context": context,
84
+ },
85
+ status_code=st.HTTP_500_INTERNAL_SERVER_ERROR,
86
+ )
64
87
 
65
- return {
66
- "message": "Execute job via RestAPI.",
67
- "result": {"run_id": rs.run_id, "parent_run_id": rs.parent_run_id},
68
- "job": job.model_dump(
69
- by_alias=True,
70
- exclude_none=False,
71
- exclude_unset=True,
72
- ),
73
- "params": params,
74
- "context": context,
75
- }
88
+ return UJSONResponse(
89
+ content={
90
+ "message": "Execute job via RestAPI successful.",
91
+ "result": {"run_id": rs.run_id, "parent_run_id": rs.parent_run_id},
92
+ "job": job.model_dump(
93
+ by_alias=True,
94
+ exclude_none=False,
95
+ exclude_unset=True,
96
+ ),
97
+ "params": params,
98
+ "context": context,
99
+ },
100
+ status_code=st.HTTP_200_OK,
101
+ )
@@ -13,14 +13,14 @@ from fastapi.responses import UJSONResponse
13
13
  from ...logs import get_audit
14
14
  from ...result import Result
15
15
 
16
- log_route = APIRouter(
16
+ router = APIRouter(
17
17
  prefix="/logs",
18
18
  tags=["logs"],
19
19
  default_response_class=UJSONResponse,
20
20
  )
21
21
 
22
22
 
23
- @log_route.get(
23
+ @router.get(
24
24
  path="/traces/",
25
25
  response_class=UJSONResponse,
26
26
  status_code=st.HTTP_200_OK,
@@ -50,7 +50,7 @@ async def get_traces(
50
50
  }
51
51
 
52
52
 
53
- @log_route.get(
53
+ @router.get(
54
54
  path="/traces/{run_id}",
55
55
  response_class=UJSONResponse,
56
56
  status_code=st.HTTP_200_OK,
@@ -77,7 +77,7 @@ async def get_trace_with_id(run_id: str):
77
77
  }
78
78
 
79
79
 
80
- @log_route.get(
80
+ @router.get(
81
81
  path="/audits/",
82
82
  response_class=UJSONResponse,
83
83
  status_code=st.HTTP_200_OK,
@@ -94,7 +94,7 @@ async def get_audits():
94
94
  }
95
95
 
96
96
 
97
- @log_route.get(
97
+ @router.get(
98
98
  path="/audits/{workflow}/",
99
99
  response_class=UJSONResponse,
100
100
  status_code=st.HTTP_200_OK,
@@ -113,7 +113,7 @@ async def get_audit_with_workflow(workflow: str):
113
113
  }
114
114
 
115
115
 
116
- @log_route.get(
116
+ @router.get(
117
117
  path="/audits/{workflow}/{release}",
118
118
  response_class=UJSONResponse,
119
119
  status_code=st.HTTP_200_OK,
@@ -140,7 +140,7 @@ async def get_audit_with_workflow_release(
140
140
  }
141
141
 
142
142
 
143
- @log_route.get(
143
+ @router.get(
144
144
  path="/audits/{workflow}/{release}/{run_id}",
145
145
  response_class=UJSONResponse,
146
146
  status_code=st.HTTP_200_OK,
@@ -5,6 +5,7 @@
5
5
  # ------------------------------------------------------------------------------
6
6
  from __future__ import annotations
7
7
 
8
+ import logging
8
9
  from dataclasses import asdict
9
10
  from datetime import datetime
10
11
  from typing import Any
@@ -16,19 +17,19 @@ from pydantic import BaseModel
16
17
 
17
18
  from ...__types import DictData
18
19
  from ...conf import Loader
19
- from ...logs import Audit, get_audit, get_logger
20
+ from ...logs import AuditModel, get_audit
20
21
  from ...result import Result
21
22
  from ...workflow import Workflow
22
23
 
23
- logger = get_logger("uvicorn.error")
24
- workflow_route = APIRouter(
24
+ logger = logging.getLogger("uvicorn.error")
25
+ router = APIRouter(
25
26
  prefix="/workflows",
26
27
  tags=["workflows"],
27
28
  default_response_class=UJSONResponse,
28
29
  )
29
30
 
30
31
 
31
- @workflow_route.get(path="/", status_code=st.HTTP_200_OK)
32
+ @router.get(path="/", status_code=st.HTTP_200_OK)
32
33
  async def get_workflows() -> DictData:
33
34
  """Return all workflow workflows that exists in config path."""
34
35
  workflows: DictData = dict(Loader.finds(Workflow))
@@ -39,7 +40,7 @@ async def get_workflows() -> DictData:
39
40
  }
40
41
 
41
42
 
42
- @workflow_route.get(path="/{name}", status_code=st.HTTP_200_OK)
43
+ @router.get(path="/{name}", status_code=st.HTTP_200_OK)
43
44
  async def get_workflow_by_name(name: str) -> DictData:
44
45
  """Return model of workflow that passing an input workflow name."""
45
46
  try:
@@ -63,7 +64,7 @@ class ExecutePayload(BaseModel):
63
64
  params: dict[str, Any]
64
65
 
65
66
 
66
- @workflow_route.post(path="/{name}/execute", status_code=st.HTTP_202_ACCEPTED)
67
+ @router.post(path="/{name}/execute", status_code=st.HTTP_202_ACCEPTED)
67
68
  async def workflow_execute(name: str, payload: ExecutePayload) -> DictData:
68
69
  """Return model of workflow that passing an input workflow name."""
69
70
  try:
@@ -88,7 +89,7 @@ async def workflow_execute(name: str, payload: ExecutePayload) -> DictData:
88
89
  return asdict(result)
89
90
 
90
91
 
91
- @workflow_route.get(path="/{name}/audits", status_code=st.HTTP_200_OK)
92
+ @router.get(path="/{name}/audits", status_code=st.HTTP_200_OK)
92
93
  async def get_workflow_audits(name: str):
93
94
  try:
94
95
  return {
@@ -109,11 +110,11 @@ async def get_workflow_audits(name: str):
109
110
  ) from None
110
111
 
111
112
 
112
- @workflow_route.get(path="/{name}/audits/{release}", status_code=st.HTTP_200_OK)
113
+ @router.get(path="/{name}/audits/{release}", status_code=st.HTTP_200_OK)
113
114
  async def get_workflow_release_audit(name: str, release: str):
114
115
  """Get Workflow audit log with an input release value."""
115
116
  try:
116
- audit: Audit = get_audit().find_audit_with_release(
117
+ audit: AuditModel = get_audit().find_audit_with_release(
117
118
  name=name,
118
119
  release=datetime.strptime(release, "%Y%m%d%H%M%S"),
119
120
  )