bisslog-flask 0.0.2__tar.gz → 0.0.3__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 (51) hide show
  1. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/PKG-INFO +5 -3
  2. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask/builder/builder_flask_app_manager.py +25 -7
  3. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask/cli/__init__.py +6 -4
  4. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask/cli/commands/build.py +2 -0
  5. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask/cli/commands/run.py +1 -1
  6. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask/initializer/bisslog_flask_http_resolver.py +53 -12
  7. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask/initializer/bisslog_flask_resolver.py +0 -1
  8. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask/initializer/bisslog_flask_ws_resolver.py +25 -9
  9. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask.egg-info/PKG-INFO +5 -3
  10. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask.egg-info/SOURCES.txt +2 -0
  11. bisslog_flask-0.0.3/bisslog_flask.egg-info/requires.txt +12 -0
  12. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/pyproject.toml +3 -2
  13. bisslog_flask-0.0.3/requirements/requirements-async.txt +1 -0
  14. bisslog_flask-0.0.3/requirements.txt +3 -0
  15. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/tests/unit/builder/test_builder_flask_app_manager.py +18 -12
  16. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/tests/unit/builder/test_builder_full.py +7 -5
  17. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/tests/unit/cli/test_build.py +2 -4
  18. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/tests/unit/cli/test_cli.py +4 -2
  19. bisslog_flask-0.0.3/tests/unit/test_http_resolver.py +268 -0
  20. bisslog_flask-0.0.3/tests/unit/test_http_resolver_lambda_fn.py +93 -0
  21. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/tests/unit/test_init_app_manager.py +25 -8
  22. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/tests/unit/test_ws_resolver.py +4 -3
  23. bisslog_flask-0.0.2/bisslog_flask.egg-info/requires.txt +0 -9
  24. bisslog_flask-0.0.2/requirements.txt +0 -3
  25. bisslog_flask-0.0.2/tests/unit/test_http_resolver.py +0 -97
  26. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/.github/workflows/publish-pypi.yml +0 -0
  27. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/.github/workflows/publish-test-pypi.yml +0 -0
  28. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/.github/workflows/receive-changes.yml +0 -0
  29. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/.gitignore +0 -0
  30. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/LICENSE +0 -0
  31. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/README.md +0 -0
  32. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask/__init__.py +0 -0
  33. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask/builder/__init__.py +0 -0
  34. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask/builder/static_python_construct_data.py +0 -0
  35. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask/cli/commands/__init__.py +0 -0
  36. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask/initializer/__init__.py +0 -0
  37. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask/initializer/init_flask_app_manager.py +1 -1
  38. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask/socket_helper/__init__.py +0 -0
  39. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask/socket_helper/socket_helper.py +0 -0
  40. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask.egg-info/dependency_links.txt +0 -0
  41. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask.egg-info/entry_points.txt +0 -0
  42. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/bisslog_flask.egg-info/top_level.txt +0 -0
  43. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/pylintrc +0 -0
  44. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/requirements/requirements-flask-optional.txt +0 -0
  45. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/setup.cfg +0 -0
  46. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/tests/__init__.py +0 -0
  47. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/tests/unit/__init__.py +0 -0
  48. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/tests/unit/builder/__init__.py +0 -0
  49. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/tests/unit/builder/test_static_python_res.py +0 -0
  50. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/tests/unit/cli/__init__.py +0 -0
  51. {bisslog_flask-0.0.2 → bisslog_flask-0.0.3}/tests/unit/cli/test_run.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bisslog_flask
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  Summary: It is an extension of the bisslog library to support processes with flask
5
5
  Author-email: Darwin Stiven Herrera Cartagena <darwinsherrerac@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/darwinhc/bisslog-flask
@@ -10,13 +10,15 @@ Classifier: Operating System :: OS Independent
10
10
  Requires-Python: >=3.7
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
- Requires-Dist: bisslog>=0.0.7
14
- Requires-Dist: bisslog-schema>=0.0.6
13
+ Requires-Dist: bisslog>=0.0.9
14
+ Requires-Dist: bisslog-schema>=0.0.10
15
15
  Requires-Dist: flask
16
16
  Provides-Extra: websocket
17
17
  Requires-Dist: flask-socketio; extra == "websocket"
18
18
  Provides-Extra: cors
19
19
  Requires-Dist: flask-cors>=6.0.0; extra == "cors"
20
+ Provides-Extra: async
21
+ Requires-Dist: Flask[async]>=2.2; extra == "async"
20
22
  Dynamic: license-file
21
23
 
22
24
  # bisslog-flask
@@ -8,6 +8,7 @@ configuration, and runtime setup.
8
8
  The generated code is returned as a full Python script and can be written to a file (e.g.,
9
9
  `flask_app.py`).
10
10
  """
11
+
11
12
  from typing import Optional, Callable
12
13
  import json
13
14
 
@@ -17,6 +18,7 @@ from bisslog_schema.schema import UseCaseInfo, TriggerHttp, TriggerWebsocket
17
18
  from bisslog_schema.setup import get_setup_metadata
18
19
  from bisslog_schema.use_case_code_inspector.use_case_code_metadata import UseCaseCodeInfo, \
19
20
  UseCaseCodeInfoClass, UseCaseCodeInfoObject
21
+
20
22
  from .static_python_construct_data import StaticPythonConstructData
21
23
 
22
24
 
@@ -147,6 +149,9 @@ if "JWT_SECRET_KEY" in os.environ:
147
149
  StaticPythonConstructData
148
150
  The generated code for the HTTP trigger.
149
151
  """
152
+ imports = {
153
+ "flask": {"jsonify"}
154
+ }
150
155
  starting_build = ""
151
156
  mapper_code_lines = []
152
157
  if trigger_info.mapper is not None:
@@ -155,7 +160,7 @@ if "JWT_SECRET_KEY" in os.environ:
155
160
  f"base={json.dumps(trigger_info.mapper)})")
156
161
  mapper_code_lines.append(f"""
157
162
  res_map = {mapper_name}.map({{
158
- "path_query": request.view_args or {{}},
163
+ "path_query": route_vars,
159
164
  "body": request.get_json(silent=True) or {{}},
160
165
  "params": request.args.to_dict(),
161
166
  "headers": request.headers,
@@ -166,20 +171,33 @@ if "JWT_SECRET_KEY" in os.environ:
166
171
 
167
172
  lines = [
168
173
  f'@app.route("{flask_path}", methods=["{method}"])',
169
- f'def {handler_name}():',
170
174
  ]
175
+
176
+ if use_case_code_info.is_coroutine:
177
+ lines.append(f"async def {handler_name}(**route_vars):")
178
+ else:
179
+ lines.append(f"def {handler_name}(**route_vars):")
180
+
171
181
  if not mapper_code_lines:
172
182
  lines.append(" kwargs = {}")
173
- lines.append(" kwargs.update(request.view_args or {})")
183
+ lines.append(" kwargs.update(route_vars)")
174
184
  lines.append(" kwargs.update(request.get_json(silent=True) or {})")
175
185
  lines.append(" kwargs.update(request.args.to_dict())")
176
186
  lines.append(" kwargs.update(dict(request.headers))")
177
- lines.append(f" return {uc_callable}(**kwargs)\n")
187
+ var_to_unpack = "kwargs"
178
188
  else:
179
189
  lines.extend(mapper_code_lines)
180
- lines.append(f' return {uc_callable}(**res_map)\n')
190
+ var_to_unpack = "res_map"
191
+
192
+ if use_case_code_info.is_coroutine:
193
+ lines.append(f' result = await {uc_callable}(**{var_to_unpack})')
194
+ else:
195
+ lines.append(f' result = {uc_callable}(**{var_to_unpack})\n')
196
+
197
+ lines.append(' return jsonify(result)\n')
181
198
 
182
- return StaticPythonConstructData(build=starting_build, body="\n".join(lines))
199
+ return StaticPythonConstructData(build=starting_build,
200
+ body="\n".join(lines), importing=imports)
183
201
 
184
202
  @staticmethod
185
203
  def _generate_use_case_code_websocket_trigger(
@@ -217,7 +235,7 @@ if "JWT_SECRET_KEY" in os.environ:
217
235
  imports = {
218
236
  use_case_code_info.module: {use_case_code_info.name},
219
237
  "flask_sock": {"Sock"},
220
- "flask": {"request"},
238
+ "flask": {"request", "jsonify"},
221
239
  "bisslog.utils.mapping": {"Mapper"},
222
240
  "json": None
223
241
  }
@@ -1,10 +1,11 @@
1
+ """Command-line interface for the `bisslog_flask` package."""
2
+ import argparse
1
3
  import os
2
4
  import sys
3
- import argparse
4
5
  import traceback
5
6
 
6
- from .commands.run import run
7
7
  from .commands.build import build_boiler_plate_flask
8
+ from .commands.run import run
8
9
 
9
10
 
10
11
  def main():
@@ -62,7 +63,8 @@ def main():
62
63
  build_parser.add_argument("--encoding", type=str, default="utf-8",
63
64
  help="File encoding (default: utf-8).")
64
65
  build_parser.add_argument("--target-filename", type=str, default="flask_app.py",
65
- help="Filename to write the generated boilerplate (default: flask_app.py)")
66
+ help="Filename to write the generated "
67
+ "boilerplate (default: flask_app.py)")
66
68
 
67
69
  args = parser.parse_args()
68
70
 
@@ -82,7 +84,7 @@ def main():
82
84
  encoding=args.encoding,
83
85
  target_filename=args.target_filename
84
86
  )
85
- except Exception as e:
87
+ except Exception as e: # pylint: disable=broad-except
86
88
  traceback.print_exc()
87
89
  print(e)
88
90
  sys.exit(1)
@@ -10,8 +10,10 @@ The generated code supports HTTP and WebSocket routes, environment-based securit
10
10
  configuration, and respects the Bisslog runtime setup defined via decorators.
11
11
  """
12
12
  from typing import Optional
13
+
13
14
  from ...builder.builder_flask_app_manager import bisslog_flask_builder
14
15
 
16
+
15
17
  def build_boiler_plate_flask(
16
18
  metadata_file: Optional[str] = None,
17
19
  use_cases_folder_path: Optional[str] = None,
@@ -18,7 +18,7 @@ def run(metadata_file: Optional[str] = None,
18
18
  secret_key: Optional[str] = None,
19
19
  jwt_secret_key: Optional[str] = None):
20
20
  """
21
- Run a Flask application using metadata and use case source.
21
+ Run a Flask application using metadata and use-case source.
22
22
 
23
23
  This function creates and runs a Flask app configured through the
24
24
  BisslogFlask integration layer. It loads metadata definitions,
@@ -16,10 +16,12 @@ Dependencies
16
16
  - bisslog_schema
17
17
  - bisslog.utils.mapping
18
18
  """
19
+ import inspect
19
20
  from copy import deepcopy
20
- from typing import Callable, Optional, Dict
21
+ from typing import Callable, Optional, Dict, Union, Awaitable, Any
21
22
 
22
23
  from flask import Flask, request, jsonify
24
+
23
25
  try:
24
26
  from flask_cors import cross_origin
25
27
  except ImportError:
@@ -82,10 +84,10 @@ class BisslogFlaskHttpResolver(BisslogFlaskResolver):
82
84
 
83
85
  @staticmethod
84
86
  def _use_case_factory(
85
- use_case_name: str,
86
- fn: Callable,
87
- mapper: Optional[Dict[str, str]] = None,
88
- trigger: Optional[TriggerHttp] = None
87
+ use_case_name: str,
88
+ fn: Callable,
89
+ mapper: Optional[Dict[str, str]] = None,
90
+ trigger: Optional[TriggerHttp] = None
89
91
  ):
90
92
  """
91
93
  Factory to produce a Flask view function with optional mapping and CORS.
@@ -109,10 +111,49 @@ class BisslogFlaskHttpResolver(BisslogFlaskResolver):
109
111
  use_case_fn_copy = deepcopy(fn)
110
112
  __mapper__ = Mapper(name=f"Mapper {use_case_name}", base=mapper) if mapper else None
111
113
 
112
- def uc(*args, **kwargs):
113
- return BisslogFlaskHttpResolver._lambda_fn(
114
- *args, fn=use_case_fn_copy, __mapper__=__mapper__, **kwargs
115
- )
114
+ is_async = (
115
+ inspect.iscoroutinefunction(fn)
116
+ or inspect.iscoroutinefunction(getattr(fn, "__call__", None))
117
+ )
118
+
119
+ if is_async:
120
+ async def uc(*args, **kwargs):
121
+ if __mapper__ is None:
122
+ more_kwargs = {}
123
+ if request.method.lower() != "get":
124
+ more_kwargs.update(request.get_json(silent=True) or {})
125
+ res = await use_case_fn_copy(*args, **kwargs, **more_kwargs)
126
+ return jsonify(res)
127
+
128
+ res_map = __mapper__.map({
129
+ "path_query": request.view_args or {},
130
+ "body": request.get_json(silent=True) or {},
131
+ "params": request.args.to_dict(),
132
+ "headers": request.headers,
133
+ })
134
+ res = await use_case_fn_copy(**res_map)
135
+ return jsonify(res)
136
+
137
+ view = uc
138
+ else:
139
+ def uc(*args, **kwargs):
140
+ if __mapper__ is None:
141
+ more_kwargs = {}
142
+ if request.method.lower() != "get":
143
+ more_kwargs.update(request.get_json(silent=True) or {})
144
+ res = use_case_fn_copy(*args, **kwargs, **more_kwargs)
145
+ return jsonify(res)
146
+
147
+ res_map = __mapper__.map({
148
+ "path_query": request.view_args or {},
149
+ "body": request.get_json(silent=True) or {},
150
+ "params": request.args.to_dict(),
151
+ "headers": request.headers,
152
+ })
153
+ res = use_case_fn_copy(**res_map)
154
+ return jsonify(res)
155
+
156
+ view = uc
116
157
 
117
158
  # Apply CORS dynamically if allowed
118
159
  if trigger and trigger.allow_cors:
@@ -124,13 +165,13 @@ class BisslogFlaskHttpResolver(BisslogFlaskResolver):
124
165
  "allow_headers": ["Content-Type", "Authorization"],
125
166
  "supports_credentials": True
126
167
  }
127
- return cross_origin(**cors_kwargs)(uc)
168
+ return cross_origin(**cors_kwargs)(view)
128
169
 
129
- return uc
170
+ return view
130
171
 
131
172
  @classmethod
132
173
  def _add_use_case(cls, app: Flask, use_case_info: UseCaseInfo, trigger: TriggerInfo,
133
- use_case_function):
174
+ use_case_function: Union[Callable[..., Any], Callable[..., Awaitable[Any]]]):
134
175
  """
135
176
  Adds an HTTP endpoint to the Flask app for a given use case.
136
177
 
@@ -9,7 +9,6 @@ from abc import ABC, abstractmethod
9
9
  from typing import Callable
10
10
 
11
11
  from bisslog_schema.schema import UseCaseInfo, TriggerInfo
12
-
13
12
  from flask import Flask
14
13
 
15
14
 
@@ -5,7 +5,9 @@ This module defines the `BisslogFlaskWebSocketResolver` class, which dynamically
5
5
  WebSocket-based use case triggers in a Flask app. The class uses metadata definitions to
6
6
  configure event routes (via `route_key`) and binds them to corresponding use case functions.
7
7
  """
8
+ import inspect
8
9
  from typing import Callable
10
+
9
11
  from flask import Flask, request
10
12
 
11
13
  try:
@@ -64,13 +66,27 @@ class BisslogFlaskWebSocketResolver(BisslogFlaskResolver):
64
66
  base=trigger_info.options.mapper
65
67
  ) if trigger_info.options.mapper else None
66
68
 
67
- @socket_io_obj.on(route_key)
68
- def on_event(data):
69
- mapped_data = mapper.map({
70
- "route_key": route_key,
71
- "connection_id": request.sid,
72
- "body": data,
73
- "headers": dict(request.headers)
74
- }) if mapper else data
69
+ is_async = inspect.iscoroutinefunction(use_case_callable)
70
+
71
+ if is_async:
72
+ @socket_io_obj.on(route_key)
73
+ async def on_event(data):
74
+ mapped_data = mapper.map({
75
+ "route_key": route_key,
76
+ "connection_id": request.sid,
77
+ "body": data,
78
+ "headers": dict(request.headers)
79
+ }) if mapper else data
80
+
81
+ return await use_case_callable(**mapped_data) if mapper else use_case_callable(data)
82
+ else:
83
+ @socket_io_obj.on(route_key)
84
+ def on_event(data):
85
+ mapped_data = mapper.map({
86
+ "route_key": route_key,
87
+ "connection_id": request.sid,
88
+ "body": data,
89
+ "headers": dict(request.headers)
90
+ }) if mapper else data
75
91
 
76
- return use_case_callable(**mapped_data) if mapper else use_case_callable(data)
92
+ return use_case_callable(**mapped_data) if mapper else use_case_callable(data)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bisslog_flask
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  Summary: It is an extension of the bisslog library to support processes with flask
5
5
  Author-email: Darwin Stiven Herrera Cartagena <darwinsherrerac@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/darwinhc/bisslog-flask
@@ -10,13 +10,15 @@ Classifier: Operating System :: OS Independent
10
10
  Requires-Python: >=3.7
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
- Requires-Dist: bisslog>=0.0.7
14
- Requires-Dist: bisslog-schema>=0.0.6
13
+ Requires-Dist: bisslog>=0.0.9
14
+ Requires-Dist: bisslog-schema>=0.0.10
15
15
  Requires-Dist: flask
16
16
  Provides-Extra: websocket
17
17
  Requires-Dist: flask-socketio; extra == "websocket"
18
18
  Provides-Extra: cors
19
19
  Requires-Dist: flask-cors>=6.0.0; extra == "cors"
20
+ Provides-Extra: async
21
+ Requires-Dist: Flask[async]>=2.2; extra == "async"
20
22
  Dynamic: license-file
21
23
 
22
24
  # bisslog-flask
@@ -28,10 +28,12 @@ bisslog_flask/initializer/bisslog_flask_ws_resolver.py
28
28
  bisslog_flask/initializer/init_flask_app_manager.py
29
29
  bisslog_flask/socket_helper/__init__.py
30
30
  bisslog_flask/socket_helper/socket_helper.py
31
+ requirements/requirements-async.txt
31
32
  requirements/requirements-flask-optional.txt
32
33
  tests/__init__.py
33
34
  tests/unit/__init__.py
34
35
  tests/unit/test_http_resolver.py
36
+ tests/unit/test_http_resolver_lambda_fn.py
35
37
  tests/unit/test_init_app_manager.py
36
38
  tests/unit/test_ws_resolver.py
37
39
  tests/unit/builder/__init__.py
@@ -0,0 +1,12 @@
1
+ bisslog>=0.0.9
2
+ bisslog-schema>=0.0.10
3
+ flask
4
+
5
+ [async]
6
+ Flask[async]>=2.2
7
+
8
+ [cors]
9
+ flask-cors>=6.0.0
10
+
11
+ [websocket]
12
+ flask-socketio
@@ -12,8 +12,8 @@ authors = [
12
12
  ]
13
13
  requires-python = ">=3.7"
14
14
  dependencies = [
15
- "bisslog>=0.0.7",
16
- "bisslog-schema>=0.0.6",
15
+ "bisslog>=0.0.9",
16
+ "bisslog-schema>=0.0.10",
17
17
  "flask"
18
18
  ]
19
19
  classifiers = [
@@ -28,6 +28,7 @@ Homepage = "https://github.com/darwinhc/bisslog-flask"
28
28
  [project.optional-dependencies]
29
29
  websocket = ["flask-socketio"]
30
30
  cors = ["flask-cors>=6.0.0"]
31
+ async = ["Flask[async]>=2.2"]
31
32
 
32
33
  [project.scripts]
33
34
  bisslog_flask = "bisslog_flask.cli:main"
@@ -0,0 +1 @@
1
+ Flask[async]>=2.2
@@ -0,0 +1,3 @@
1
+ bisslog>=0.0.7
2
+ bisslog-schema>=0.0.9
3
+ flask
@@ -1,15 +1,15 @@
1
1
  # test_builder_flask_app_manager.py
2
2
 
3
- import pytest
4
- import json
5
3
  from unittest.mock import patch, MagicMock
6
- from bisslog_flask.builder.builder_flask_app_manager import BuilderFlaskAppManager
7
- from bisslog_flask.builder.static_python_construct_data import StaticPythonConstructData
4
+
5
+ import pytest
8
6
  from bisslog_schema.schema import TriggerHttp, TriggerWebsocket
9
7
  from bisslog_schema.use_case_code_inspector.use_case_code_metadata import (
10
8
  UseCaseCodeInfoClass, UseCaseCodeInfoObject, UseCaseCodeInfo
11
9
  )
12
10
 
11
+ from bisslog_flask.builder.builder_flask_app_manager import BuilderFlaskAppManager
12
+
13
13
 
14
14
  @pytest.mark.parametrize("n_params,expected", [
15
15
  (0, "setup_func()"),
@@ -41,14 +41,16 @@ def test_get_bisslog_setup_none(mock_get_setup):
41
41
 
42
42
 
43
43
  def test_generate_use_case_code_build_class():
44
- uc = UseCaseCodeInfoClass(name="my_uc", docs="", module="mymodule", class_name="MyClass")
44
+ uc = UseCaseCodeInfoClass(name="my_uc", docs="", module="mymodule", class_name="MyClass",
45
+ is_coroutine=False)
45
46
  name, result = BuilderFlaskAppManager._generate_use_case_code_build(uc)
46
47
  assert name == "my_uc_uc"
47
48
  assert "MyClass()" in result.build
48
49
 
49
50
 
50
51
  def test_generate_use_case_code_build_object():
51
- uc = UseCaseCodeInfoObject(name="my_uc", docs="", module="mymodule", var_name="uc_var")
52
+ uc = UseCaseCodeInfoObject(name="my_uc", docs="", module="mymodule", var_name="uc_var",
53
+ is_coroutine=False)
52
54
  name, result = BuilderFlaskAppManager._generate_use_case_code_build(uc)
53
55
  assert name == "uc_var"
54
56
  assert result.build == ""
@@ -57,22 +59,24 @@ def test_generate_use_case_code_build_object():
57
59
  def test_generate_use_case_code_build_invalid_type():
58
60
  with pytest.raises(ValueError):
59
61
  class Invalid(UseCaseCodeInfo): pass
60
- BuilderFlaskAppManager._generate_use_case_code_build(Invalid("x", "", ""))
62
+ BuilderFlaskAppManager._generate_use_case_code_build(Invalid("x", "", "", False))
61
63
 
62
64
 
63
65
  def test_generate_use_case_code_http_trigger_without_mapper():
64
66
  trigger = TriggerHttp(path="/hello", method="POST", mapper=None)
65
- uc_info = UseCaseCodeInfoClass(name="myuc", docs="", module="mymod", class_name="UCClass")
67
+ uc_info = UseCaseCodeInfoClass(name="myuc", docs="", module="mymod", class_name="UCClass",
68
+ is_coroutine=False)
66
69
  result = BuilderFlaskAppManager._generate_use_case_code_http_trigger(
67
70
  "my_uc", "my_uc_uc", uc_info, trigger, 1)
68
- assert "def my_uc_handler_1()" in result.body
71
+ assert "def my_uc_handler_1(**route_vars)" in result.body
69
72
  assert "my_uc_uc(**kwargs)" in result.body
70
73
  assert "request.get_json" in result.body
71
74
 
72
75
 
73
76
  def test_generate_use_case_code_http_trigger_with_mapper():
74
77
  trigger = TriggerHttp(path="/hi", method="GET", mapper={"body": {"x": "int"}})
75
- uc_info = UseCaseCodeInfoClass(name="myuc", docs="", module="mymod", class_name="UCClass")
78
+ uc_info = UseCaseCodeInfoClass(name="myuc", docs="", module="mymod", class_name="UCClass",
79
+ is_coroutine=False)
76
80
  result = BuilderFlaskAppManager._generate_use_case_code_http_trigger(
77
81
  "my_uc", "my_uc_uc", uc_info, trigger, 2)
78
82
  assert "res_map" in result.body
@@ -81,7 +85,8 @@ def test_generate_use_case_code_http_trigger_with_mapper():
81
85
 
82
86
  def test_generate_use_case_code_websocket_trigger_with_mapper():
83
87
  trigger = TriggerWebsocket(route_key="room", mapper={"body": {"msg": "str"}})
84
- uc_info = UseCaseCodeInfoObject(name="ws_uc", docs="", module="wsmod", var_name="ws_callable")
88
+ uc_info = UseCaseCodeInfoObject(name="ws_uc", docs="", module="wsmod", var_name="ws_callable",
89
+ is_coroutine=False)
85
90
  result = BuilderFlaskAppManager._generate_use_case_code_websocket_trigger(
86
91
  "chat", "ws_callable", uc_info, trigger, 0)
87
92
  assert "res_map = chat_ws_mapper_0.map" in result.build
@@ -91,7 +96,8 @@ def test_generate_use_case_code_websocket_trigger_with_mapper():
91
96
 
92
97
  def test_generate_use_case_code_websocket_trigger_without_mapper():
93
98
  trigger = TriggerWebsocket(route_key=None, mapper=None)
94
- uc_info = UseCaseCodeInfoObject(name="ws_uc", docs="", module="wsmod", var_name="ws_callable")
99
+ uc_info = UseCaseCodeInfoObject(name="ws_uc", docs="", module="wsmod", var_name="ws_callable",
100
+ is_coroutine=False)
95
101
  result = BuilderFlaskAppManager._generate_use_case_code_websocket_trigger(
96
102
  "chat", "ws_callable", uc_info, trigger, 1)
97
103
  assert "payload = json.loads(data)" in result.build
@@ -1,14 +1,15 @@
1
1
  # test_builder_flask_app_manager_call.py
2
2
 
3
- import pytest
4
3
  from unittest.mock import patch, MagicMock
5
- from bisslog_flask.builder.builder_flask_app_manager import BuilderFlaskAppManager
6
- from bisslog_flask.builder.static_python_construct_data import StaticPythonConstructData
4
+
7
5
  from bisslog_schema.schema import TriggerHttp, TriggerWebsocket, UseCaseInfo
8
6
  from bisslog_schema.use_case_code_inspector.use_case_code_metadata import (
9
- UseCaseCodeInfoClass, UseCaseCodeInfoObject
7
+ UseCaseCodeInfoClass
10
8
  )
11
9
 
10
+ from bisslog_flask.builder.builder_flask_app_manager import BuilderFlaskAppManager
11
+ from bisslog_flask.builder.static_python_construct_data import StaticPythonConstructData
12
+
12
13
 
13
14
  @patch("bisslog_flask.builder.builder_flask_app_manager.read_full_service_metadata")
14
15
  @patch.object(BuilderFlaskAppManager, "_get_bisslog_setup")
@@ -41,7 +42,8 @@ def test_call_generates_complete_flask_code(
41
42
 
42
43
  discovered_use_cases = {
43
44
  "my_use_case": UseCaseCodeInfoClass(
44
- name="my_use_case", docs=None, module="uc_module", class_name="UseCase"
45
+ name="my_use_case", docs=None, module="uc_module", class_name="UseCase",
46
+ is_coroutine=False
45
47
  )
46
48
  }
47
49
 
@@ -1,8 +1,6 @@
1
- # test_cli_build_boilerplate.py
2
-
3
- import builtins
4
- import pytest
1
+ """Tests for the build command."""
5
2
  from unittest.mock import patch, mock_open
3
+
6
4
  from bisslog_flask.cli.commands.build import build_boiler_plate_flask
7
5
 
8
6
 
@@ -1,7 +1,9 @@
1
- import pytest
2
- import sys
3
1
  import os
2
+ import sys
4
3
  from unittest.mock import patch, MagicMock
4
+
5
+ import pytest
6
+
5
7
  from bisslog_flask import cli as cli_module
6
8
 
7
9
 
@@ -0,0 +1,268 @@
1
+ """Tests for bisslog_flask.initializer.bisslog_flask_http_resolver."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ from unittest.mock import Mock
7
+
8
+ import pytest
9
+ import flask
10
+ from flask import Flask
11
+
12
+ from bisslog_schema.schema import UseCaseInfo, TriggerHttp, TriggerInfo
13
+ from bisslog_schema.schema.triggers.trigger_mappable import TriggerMappable
14
+
15
+ from bisslog_flask.initializer.bisslog_flask_http_resolver import BisslogFlaskHttpResolver
16
+
17
+
18
+ pytestmark = pytest.mark.skipif(
19
+ tuple(map(int, flask.__version__.split(".")[:2])) < (2, 2),
20
+ reason="Async views need Flask >= 2.2",
21
+ )
22
+
23
+ @pytest.fixture
24
+ def flask_app():
25
+ app = Flask(__name__)
26
+ return app
27
+
28
+
29
+ @pytest.fixture
30
+ def resolver():
31
+ return BisslogFlaskHttpResolver()
32
+
33
+
34
+ def make_uc_info(key="uc"):
35
+ return UseCaseInfo(
36
+ keyname=key, name=key, description="", type="sync", triggers=[]
37
+ )
38
+
39
+
40
+
41
+ def test_register_get_route_without_mapper(flask_app, resolver):
42
+ def mock_uc():
43
+ return {"status": "ok"}
44
+
45
+ info = make_uc_info("test_uc")
46
+ trig_http = TriggerHttp(method="GET", path="/test", allow_cors=False)
47
+ trig = TriggerInfo(keyname="t", type="http", options=trig_http)
48
+
49
+ resolver(flask_app, info, trig, mock_uc)
50
+
51
+ client = flask_app.test_client()
52
+ r = client.get("/test")
53
+
54
+ assert r.status_code == 200
55
+ assert r.json == {"status": "ok"}
56
+
57
+
58
+ def test_register_post_route_with_json(flask_app, resolver):
59
+ def mock_uc(**data):
60
+ return {"echo": data.get("name")}
61
+
62
+ info = make_uc_info("echo_uc")
63
+ trig_http = TriggerHttp(method="POST", path="/echo", allow_cors=False)
64
+ trig = TriggerInfo(keyname="t", type="http", options=trig_http)
65
+
66
+ resolver(flask_app, info, trig, mock_uc)
67
+
68
+ client = flask_app.test_client()
69
+ r = client.post("/echo", json={"name": "ChatGPT"})
70
+
71
+ assert r.status_code == 200
72
+ assert r.json == {"echo": "ChatGPT"}
73
+
74
+
75
+ def test_register_post_route_without_body_ok(flask_app, resolver):
76
+
77
+ def mock_uc(**kwargs):
78
+ return {"kwargs": kwargs}
79
+
80
+ info = make_uc_info("no_body")
81
+ trig_http = TriggerHttp(method="POST", path="/no-body", allow_cors=False)
82
+ trig = TriggerInfo(keyname="t", type="http", options=trig_http)
83
+
84
+ resolver(flask_app, info, trig, mock_uc)
85
+
86
+ client = flask_app.test_client()
87
+ r = client.post("/no-body")
88
+
89
+ assert r.status_code == 200
90
+ assert r.json == {"kwargs": {}}
91
+
92
+
93
+
94
+ def test_register_post_route_with_mapper(flask_app, resolver):
95
+ def mock_uc(*, a=None, b=None, c=None, d=None, e=None, f=None):
96
+ return {"a": a, "b": b, "c": c, "d": d, "e": e, "f": f}
97
+
98
+ info = make_uc_info("echo_uc")
99
+ mapper = {
100
+ "body.algo1": "a",
101
+ "body.algo2": "b",
102
+ "path_query.algo3": "c",
103
+ "headers.algo4": "d",
104
+ "params.algo5": "e",
105
+ "params.algo6": "f",
106
+ }
107
+ trig_http = TriggerHttp(
108
+ method="POST",
109
+ path="/echo/{algo3}",
110
+ allow_cors=False,
111
+ mapper=mapper,
112
+ )
113
+ assert isinstance(trig_http, TriggerMappable)
114
+ trig = TriggerInfo(keyname="t", type="http", options=trig_http)
115
+
116
+ resolver(flask_app, info, trig, mock_uc)
117
+
118
+ client = flask_app.test_client()
119
+ r = client.post(
120
+ "/echo/something",
121
+ json={"algo1": 2356, "algo2": "casa"},
122
+ headers=[("algo4", "prueba4")],
123
+ query_string={"algo5": 7554, "algo6": "prueba6"},
124
+ )
125
+
126
+ assert r.status_code == 200
127
+ payload = r.get_json()
128
+ assert payload["a"] == 2356
129
+ assert payload["b"] == "casa"
130
+ assert payload["c"] == "something"
131
+ assert payload["d"] == "prueba4"
132
+ assert payload["e"] == "7554"
133
+ assert payload["f"] == "prueba6"
134
+
135
+
136
+
137
+ def test_register_async_function(flask_app, resolver):
138
+ async def mock_uc(**data):
139
+ await asyncio.sleep(0)
140
+ return {"ok": True, "data": data}
141
+
142
+ info = make_uc_info("async_fn")
143
+ trig_http = TriggerHttp(method="POST", path="/async-fn", allow_cors=False)
144
+ trig = TriggerInfo(keyname="t", type="http", options=trig_http)
145
+
146
+ resolver(flask_app, info, trig, mock_uc)
147
+
148
+ client = flask_app.test_client()
149
+ r = client.post("/async-fn", json={"a": 1})
150
+ assert r.status_code == 200
151
+ assert r.json == {"ok": True, "data": {"a": 1}}
152
+
153
+
154
+ def test_register_async_callable_object(flask_app, resolver):
155
+ class UC:
156
+ async def __call__(self, *, a=None):
157
+ await asyncio.sleep(0)
158
+ return {"a": a}
159
+
160
+ info = make_uc_info("async_obj")
161
+ trig_http = TriggerHttp(
162
+ method="POST",
163
+ path="/async-obj",
164
+ allow_cors=False,
165
+ mapper={"body.a": "a"},
166
+ )
167
+ trig = TriggerInfo(keyname="t", type="http", options=trig_http)
168
+
169
+ resolver(flask_app, info, trig, UC())
170
+
171
+ client = flask_app.test_client()
172
+ r = client.post("/async-obj", json={"a": 7})
173
+ assert r.status_code == 200
174
+ assert r.json == {"a": 7}
175
+
176
+
177
+
178
+ def test_invalid_trigger_type_is_ignored(flask_app, resolver):
179
+ trig = TriggerInfo(keyname="invalid", type="websocket", options=Mock())
180
+ mock_uc = Mock()
181
+ info = make_uc_info("invalid_uc")
182
+
183
+ resolver(flask_app, info, trig, mock_uc)
184
+
185
+ client = flask_app.test_client()
186
+ r = client.get("/invalid")
187
+ assert r.status_code == 404
188
+
189
+
190
+ def test_endpoint_name_contains_key_and_path(flask_app, resolver):
191
+ def mock_uc(id):
192
+ return {"ok": 1}
193
+
194
+ info = make_uc_info("keyX")
195
+ trig_http = TriggerHttp(method="GET", path="/items/{id}", allow_cors=False)
196
+ trig = TriggerInfo(keyname="t", type="http", options=trig_http)
197
+
198
+ resolver(flask_app, info, trig, mock_uc)
199
+
200
+
201
+ expected_rule = "keyX " + "/items/<id>"
202
+ found = any(r.rule == "/items/<id>" and r.endpoint == expected_rule for r in flask_app.url_map.iter_rules())
203
+ assert found
204
+
205
+ client = flask_app.test_client()
206
+ r = client.get("/items/42")
207
+ assert r.status_code == 200
208
+ assert r.json == {"ok": 1}
209
+
210
+
211
+
212
+ def test_cors_applied_when_allowed(monkeypatch, flask_app, resolver):
213
+
214
+ captured = {}
215
+
216
+ def fake_cross_origin(**kwargs):
217
+ captured.update(kwargs)
218
+ def decorator(fn):
219
+ return fn
220
+ return decorator
221
+
222
+
223
+ import bisslog_flask.initializer.bisslog_flask_http_resolver as mod
224
+ monkeypatch.setattr(mod, "cross_origin", fake_cross_origin, raising=True)
225
+
226
+ def mock_uc():
227
+ return {"ok": True}
228
+
229
+ info = make_uc_info("cors_uc")
230
+ trig_http = TriggerHttp(
231
+ method="GET",
232
+ path="/cors",
233
+ allow_cors=True,
234
+ allowed_origins=["https://a.com", "https://b.com"],
235
+ )
236
+ trig = TriggerInfo(keyname="t", type="http", options=trig_http)
237
+
238
+ resolver(flask_app, info, trig, mock_uc)
239
+
240
+
241
+ assert captured["origins"] == ["https://a.com", "https://b.com"]
242
+ assert captured["methods"] == ["GET"]
243
+ assert captured["supports_credentials"] is True
244
+ assert "allow_headers" in captured
245
+
246
+ client = flask_app.test_client()
247
+ r = client.get("/cors")
248
+ assert r.status_code == 200
249
+ assert r.json == {"ok": True}
250
+
251
+
252
+ def test_cors_raises_when_flask_cors_missing(monkeypatch, flask_app):
253
+
254
+ import bisslog_flask.initializer.bisslog_flask_http_resolver as mod
255
+ monkeypatch.setattr(mod, "cross_origin", None, raising=True)
256
+
257
+ resolver = BisslogFlaskHttpResolver()
258
+
259
+ def mock_uc():
260
+ return {"ok": True}
261
+
262
+ info = make_uc_info("cors_missing")
263
+ trig_http = TriggerHttp(method="GET", path="/cors-missing", allow_cors=True)
264
+ trig = TriggerInfo(keyname="t", type="http", options=trig_http)
265
+
266
+ with pytest.raises(ImportError):
267
+
268
+ resolver(flask_app, info, trig, mock_uc)
@@ -0,0 +1,93 @@
1
+
2
+ from __future__ import annotations
3
+
4
+ from flask import Flask
5
+ from unittest.mock import Mock
6
+ import flask
7
+ import pytest
8
+
9
+ from bisslog_flask.initializer.bisslog_flask_http_resolver import BisslogFlaskHttpResolver
10
+
11
+ pytestmark = pytest.mark.skipif(
12
+ tuple(map(int, flask.__version__.split(".")[:2])) < (2, 2),
13
+ reason="Async views need Flask >= 2.2",
14
+ )
15
+
16
+ @pytest.fixture
17
+ def app():
18
+ return Flask(__name__)
19
+
20
+
21
+ def test_lambda_fn_without_mapper_get_does_not_include_body(app):
22
+ called = {}
23
+
24
+ def uc(**kwargs):
25
+ called.update(kwargs)
26
+ return {"ok": True}
27
+
28
+ with app.test_request_context("/items/42?q=abc", method="GET"):
29
+ resp = BisslogFlaskHttpResolver._lambda_fn(fn=uc, __mapper__=None, id="42")
30
+ assert resp.mimetype == "application/json"
31
+ assert resp.get_json() == {"ok": True}
32
+
33
+ assert called == {"id": "42"}
34
+
35
+
36
+ def test_lambda_fn_without_mapper_post_includes_body(app):
37
+ captured = {}
38
+
39
+ def uc(**kwargs):
40
+ captured.update(kwargs)
41
+ return {"received": kwargs}
42
+
43
+ with app.test_request_context("/echo", method="POST", json={"a": 1, "b": "x"}):
44
+ resp = BisslogFlaskHttpResolver._lambda_fn(fn=uc, __mapper__=None)
45
+ assert resp.status_code == 200
46
+ assert resp.get_json() == {"received": {"a": 1, "b": "x"}}
47
+
48
+ assert captured == {"a": 1, "b": "x"}
49
+
50
+
51
+ def test_lambda_fn_with_mapper_maps_all_parts_and_calls_uc(app):
52
+ class FakeMapper:
53
+ def __init__(self):
54
+ self.last_input = None
55
+ def map(self, payload):
56
+ self.last_input = payload
57
+ return {"x": 99}
58
+
59
+ mapper = FakeMapper()
60
+ uc = Mock(return_value={"ok": True})
61
+
62
+ with app.test_request_context(
63
+ "/do/7?key=vv",
64
+ method="POST",
65
+ json={"foo": 1},
66
+ headers={"X-Test": "hdr"},
67
+ ):
68
+ from flask import request
69
+ request.view_args = {"id": "7"}
70
+
71
+ resp = BisslogFlaskHttpResolver._lambda_fn(fn=uc, __mapper__=mapper)
72
+
73
+ assert resp.status_code == 200
74
+ assert resp.get_json() == {"ok": True}
75
+
76
+ inp = mapper.last_input
77
+ assert set(inp.keys()) == {"path_query", "body", "params", "headers"}
78
+ assert inp["path_query"] == {"id": "7"}
79
+ assert inp["body"] == {"foo": 1}
80
+ assert inp["params"] == {"key": "vv"}
81
+ assert inp["headers"].get("X-Test") == "hdr"
82
+
83
+ uc.assert_called_once_with(x=99)
84
+
85
+
86
+ def test_lambda_fn_returns_json_response_type(app):
87
+ def uc():
88
+ return {"hello": "world"}
89
+
90
+ with app.test_request_context("/hi", method="GET"):
91
+ resp = BisslogFlaskHttpResolver._lambda_fn(fn=uc, __mapper__=None)
92
+ assert resp.mimetype == "application/json"
93
+ assert resp.get_json() == {"hello": "world"}
@@ -1,13 +1,14 @@
1
- import pytest
2
- from flask import Flask
1
+ from types import SimpleNamespace
3
2
  from unittest.mock import MagicMock
4
3
 
5
- from bisslog_flask.initializer.init_flask_app_manager import InitFlaskAppManager
4
+ import pytest
6
5
  from bisslog_schema.schema import TriggerInfo
7
6
  from bisslog_schema.schema.triggers.trigger_http import TriggerHttp
8
7
  from bisslog_schema.schema.triggers.trigger_websocket import TriggerWebsocket
9
8
  from bisslog_schema.schema.use_case_info import UseCaseInfo
10
- from types import SimpleNamespace
9
+ from flask import Flask
10
+
11
+ from bisslog_flask.initializer.init_flask_app_manager import InitFlaskAppManager
11
12
 
12
13
 
13
14
  @pytest.fixture
@@ -23,6 +24,18 @@ def fake_use_case_info():
23
24
  triggers=[http_trigger, ws_trigger]
24
25
  )
25
26
 
27
+ @pytest.fixture
28
+ def fake_use_case_info_with_async():
29
+ http_trigger = TriggerInfo(keyname="http_trigger", type="http", options=TriggerHttp(method="GET", path="/test2"))
30
+
31
+ return UseCaseInfo(
32
+ keyname="sample_coroutine_uc",
33
+ name="sample_coroutine_uc",
34
+ description="desc",
35
+ type="async",
36
+ triggers=[http_trigger]
37
+ )
38
+
26
39
  @pytest.fixture
27
40
  def fake_force_import():
28
41
  force_import = MagicMock()
@@ -30,13 +43,17 @@ def fake_force_import():
30
43
 
31
44
 
32
45
  @pytest.fixture
33
- def mock_read_service_info_with_code(fake_use_case_info):
46
+ def mock_read_service_info_with_code(fake_use_case_info, fake_use_case_info_with_async):
47
+
48
+ async def mock_coroutine(*args, **kwargs):
49
+ return {"sample_coroutine_res": 23}
50
+
34
51
  return SimpleNamespace(
35
52
  declared_metadata=SimpleNamespace(
36
53
  name="TestService",
37
- use_cases={"sample_uc": fake_use_case_info}
54
+ use_cases={"sample_uc": fake_use_case_info, "sample_coroutine_uc": fake_use_case_info_with_async}
38
55
  ),
39
- discovered_use_cases={"sample_uc": lambda: {"ok": True}}
56
+ discovered_use_cases={"sample_uc": lambda: {"ok": True}, "sample_coroutine_uc": mock_coroutine},
40
57
  )
41
58
 
42
59
 
@@ -57,7 +74,7 @@ def test_init_flask_app_registers_routes(monkeypatch, fake_use_case_info,
57
74
 
58
75
  # Assert
59
76
  assert isinstance(app, Flask)
60
- mock_http.assert_called_once()
77
+ assert mock_http.call_count == 2
61
78
  mock_ws.assert_called_once()
62
79
 
63
80
 
@@ -1,11 +1,12 @@
1
+ from unittest.mock import Mock, MagicMock
2
+
1
3
  import pytest
4
+ from bisslog_schema.schema import UseCaseInfo, TriggerInfo, TriggerConsumer
5
+ from bisslog_schema.schema.triggers.trigger_websocket import TriggerWebsocket
2
6
  from flask import Flask
3
7
  from flask_socketio import SocketIO
4
- from unittest.mock import Mock, MagicMock
5
8
 
6
9
  from bisslog_flask.initializer.bisslog_flask_ws_resolver import BisslogFlaskWebSocketResolver
7
- from bisslog_schema.schema import UseCaseInfo, TriggerInfo, TriggerConsumer
8
- from bisslog_schema.schema.triggers.trigger_websocket import TriggerWebsocket
9
10
 
10
11
 
11
12
  @pytest.fixture
@@ -1,9 +0,0 @@
1
- bisslog>=0.0.7
2
- bisslog-schema>=0.0.6
3
- flask
4
-
5
- [cors]
6
- flask-cors>=6.0.0
7
-
8
- [websocket]
9
- flask-socketio
@@ -1,3 +0,0 @@
1
- bisslog>=0.0.7
2
- bisslog-schema>=0.0.6
3
- flask
@@ -1,97 +0,0 @@
1
- import pytest
2
- from flask import Flask
3
- from unittest.mock import Mock
4
-
5
- from bisslog_flask.initializer.bisslog_flask_http_resolver import BisslogFlaskHttpResolver
6
- from bisslog_schema.schema import UseCaseInfo, TriggerHttp, TriggerInfo
7
-
8
-
9
- @pytest.fixture
10
- def flask_app():
11
- app = Flask(__name__)
12
- return app
13
-
14
-
15
- @pytest.fixture
16
- def resolver():
17
- return BisslogFlaskHttpResolver()
18
-
19
-
20
- def test_register_get_route_without_mapper(flask_app, resolver):
21
- def mock_uc():
22
- return {"status": "ok"}
23
-
24
- use_case_info = UseCaseInfo(
25
- keyname="test_uc", name="Test UC", description="", type="sync", triggers=[]
26
- )
27
- trigger_http = TriggerHttp(method="GET", path="/test", allow_cors=False)
28
- trigger = TriggerInfo(keyname="test_trigger", type="http", options=trigger_http)
29
-
30
- resolver(flask_app, use_case_info, trigger, mock_uc)
31
-
32
- client = flask_app.test_client()
33
- response = client.get("/test")
34
-
35
- assert response.status_code == 200
36
- assert response.json == {"status": "ok"}
37
-
38
-
39
- def test_register_post_route_with_json(flask_app, resolver):
40
- def mock_uc(**data):
41
- return {"echo": data.get("name")}
42
-
43
- use_case_info = UseCaseInfo(
44
- keyname="echo_uc", name="Echo UC", description="", type="sync", triggers=[]
45
- )
46
- trigger_http = TriggerHttp(method="POST", path="/echo", allow_cors=False)
47
- trigger = TriggerInfo(keyname="echo_trigger", type="http", options=trigger_http)
48
-
49
- resolver(flask_app, use_case_info, trigger, mock_uc)
50
-
51
- client = flask_app.test_client()
52
- response = client.post("/echo", json={"name": "ChatGPT"})
53
-
54
- assert response.status_code == 200
55
- assert response.json == {"echo": "ChatGPT"}
56
-
57
-
58
- def test_register_post_route_with_mapper(flask_app, resolver):
59
- def mock_uc(*, a = None, b = None, c = None, d = None, e = None, f = None):
60
- return {"a": a, "b": b, "c": c, "d": d, "e": e, "f": f}
61
-
62
- use_case_info = UseCaseInfo(
63
- keyname="echo_uc", name="Echo UC", description="", type="sync", triggers=[]
64
- )
65
- trigger_http = TriggerHttp(method="POST", path="/echo/{algo3}", allow_cors=False,
66
- mapper={"body.algo1": "a", "body.algo2": "b",
67
- "path_query.algo3": "c", "headers.algo4": "d",
68
- "params.algo5": "e", "params.algo6": "f"})
69
- trigger = TriggerInfo(keyname="echo_trigger", type="http", options=trigger_http)
70
-
71
- resolver(flask_app, use_case_info, trigger, mock_uc)
72
-
73
- client = flask_app.test_client()
74
- response = client.post("/echo/something", json={"algo1": 2356, "algo2": "casa"},
75
- headers=[("algo4", "prueba4")], query_string={"algo5": 7554, "algo6": "prueba6"})
76
-
77
- assert response.status_code == 200
78
- assert response.json["a"] == 2356
79
- assert response.json["b"] == "casa"
80
- assert response.json["c"] == "something"
81
- assert response.json["d"] == "prueba4"
82
- assert response.json["e"] == "7554"
83
- assert response.json["f"] == "prueba6"
84
-
85
- def test_invalid_trigger_type_is_ignored(flask_app, resolver):
86
- trigger = TriggerInfo(keyname="invalid", type="websocket", options=Mock())
87
- mock_uc = Mock()
88
- use_case_info = UseCaseInfo(
89
- keyname="invalid_uc", name="", description="", type="sync", triggers=[]
90
- )
91
-
92
- resolver(flask_app, use_case_info, trigger, mock_uc)
93
-
94
- client = flask_app.test_client()
95
- response = client.get("/invalid")
96
-
97
- assert response.status_code == 404
File without changes
File without changes
File without changes
@@ -19,8 +19,8 @@ from typing import Optional, Callable
19
19
 
20
20
  from bisslog_schema import read_service_info_with_code
21
21
  from bisslog_schema.eager_import_module_or_package import EagerImportModulePackage
22
- from bisslog_schema.setup import run_setup
23
22
  from bisslog_schema.schema import UseCaseInfo, TriggerHttp, TriggerWebsocket
23
+ from bisslog_schema.setup import run_setup
24
24
  from flask import Flask
25
25
 
26
26
  from .bisslog_flask_http_resolver import BisslogFlaskHttpResolver
File without changes
File without changes