dapr-ext-fastapi-dev 1.15.0.dev43__tar.gz → 1.16.0.dev48__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.
- {dapr-ext-fastapi-dev-1.15.0.dev43 → dapr_ext_fastapi_dev-1.16.0.dev48}/PKG-INFO +8 -4
- {dapr-ext-fastapi-dev-1.15.0.dev43 → dapr_ext_fastapi_dev-1.16.0.dev48}/dapr/ext/fastapi/__init__.py +0 -1
- {dapr-ext-fastapi-dev-1.15.0.dev43 → dapr_ext_fastapi_dev-1.16.0.dev48}/dapr/ext/fastapi/actor.py +2 -2
- {dapr-ext-fastapi-dev-1.15.0.dev43 → dapr_ext_fastapi_dev-1.16.0.dev48}/dapr/ext/fastapi/app.py +1 -0
- {dapr-ext-fastapi-dev-1.15.0.dev43 → dapr_ext_fastapi_dev-1.16.0.dev48}/dapr/ext/fastapi/version.py +1 -1
- {dapr-ext-fastapi-dev-1.15.0.dev43 → dapr_ext_fastapi_dev-1.16.0.dev48}/dapr_ext_fastapi_dev.egg-info/PKG-INFO +8 -4
- {dapr-ext-fastapi-dev-1.15.0.dev43 → dapr_ext_fastapi_dev-1.16.0.dev48}/dapr_ext_fastapi_dev.egg-info/SOURCES.txt +3 -1
- {dapr-ext-fastapi-dev-1.15.0.dev43 → dapr_ext_fastapi_dev-1.16.0.dev48}/dapr_ext_fastapi_dev.egg-info/requires.txt +1 -1
- {dapr-ext-fastapi-dev-1.15.0.dev43 → dapr_ext_fastapi_dev-1.16.0.dev48}/setup.cfg +1 -1
- dapr_ext_fastapi_dev-1.16.0.dev48/tests/test_app.py +156 -0
- dapr_ext_fastapi_dev-1.16.0.dev48/tests/test_dapractor.py +88 -0
- {dapr-ext-fastapi-dev-1.15.0.dev43 → dapr_ext_fastapi_dev-1.16.0.dev48}/LICENSE +0 -0
- {dapr-ext-fastapi-dev-1.15.0.dev43 → dapr_ext_fastapi_dev-1.16.0.dev48}/README.rst +0 -0
- {dapr-ext-fastapi-dev-1.15.0.dev43 → dapr_ext_fastapi_dev-1.16.0.dev48}/dapr_ext_fastapi_dev.egg-info/dependency_links.txt +0 -0
- {dapr-ext-fastapi-dev-1.15.0.dev43 → dapr_ext_fastapi_dev-1.16.0.dev48}/dapr_ext_fastapi_dev.egg-info/top_level.txt +0 -0
- {dapr-ext-fastapi-dev-1.15.0.dev43 → dapr_ext_fastapi_dev-1.16.0.dev48}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: dapr-ext-fastapi-dev
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.16.0.dev48
|
|
4
4
|
Summary: The developmental release for Dapr FastAPI extension.
|
|
5
5
|
Home-page: https://dapr.io/
|
|
6
6
|
Author: Dapr Authors
|
|
@@ -8,7 +8,6 @@ Author-email: daprweb@microsoft.com
|
|
|
8
8
|
License: Apache
|
|
9
9
|
Project-URL: Documentation, https://github.com/dapr/docs
|
|
10
10
|
Project-URL: Source, https://github.com/dapr/python-sdk
|
|
11
|
-
Platform: UNKNOWN
|
|
12
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
12
|
Classifier: Intended Audience :: Developers
|
|
14
13
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -21,6 +20,11 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
21
|
Requires-Python: >=3.9
|
|
23
22
|
License-File: LICENSE
|
|
23
|
+
Requires-Dist: dapr>=1.16.0.dev
|
|
24
|
+
Requires-Dist: uvicorn>=0.11.6
|
|
25
|
+
Requires-Dist: fastapi>=0.60.1
|
|
26
|
+
Dynamic: description
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
Dynamic: summary
|
|
24
29
|
|
|
25
30
|
This is the developmental release for Dapr FastAPI extension.
|
|
26
|
-
|
{dapr-ext-fastapi-dev-1.15.0.dev43 → dapr_ext_fastapi_dev-1.16.0.dev48}/dapr/ext/fastapi/actor.py
RENAMED
|
@@ -13,12 +13,12 @@ See the License for the specific language governing permissions and
|
|
|
13
13
|
limitations under the License.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
from typing import Any, Optional, Type
|
|
16
|
+
from typing import Any, List, Optional, Type
|
|
17
17
|
|
|
18
18
|
from dapr.actor import Actor, ActorRuntime
|
|
19
19
|
from dapr.clients.exceptions import ERROR_CODE_UNKNOWN, DaprInternalError
|
|
20
20
|
from dapr.serializers import DefaultJSONSerializer
|
|
21
|
-
from fastapi import
|
|
21
|
+
from fastapi import APIRouter, FastAPI, Request, Response, status # type: ignore
|
|
22
22
|
from fastapi.logger import logger
|
|
23
23
|
from fastapi.responses import JSONResponse
|
|
24
24
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: dapr-ext-fastapi-dev
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.16.0.dev48
|
|
4
4
|
Summary: The developmental release for Dapr FastAPI extension.
|
|
5
5
|
Home-page: https://dapr.io/
|
|
6
6
|
Author: Dapr Authors
|
|
@@ -8,7 +8,6 @@ Author-email: daprweb@microsoft.com
|
|
|
8
8
|
License: Apache
|
|
9
9
|
Project-URL: Documentation, https://github.com/dapr/docs
|
|
10
10
|
Project-URL: Source, https://github.com/dapr/python-sdk
|
|
11
|
-
Platform: UNKNOWN
|
|
12
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
12
|
Classifier: Intended Audience :: Developers
|
|
14
13
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -21,6 +20,11 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
21
|
Requires-Python: >=3.9
|
|
23
22
|
License-File: LICENSE
|
|
23
|
+
Requires-Dist: dapr>=1.16.0.dev
|
|
24
|
+
Requires-Dist: uvicorn>=0.11.6
|
|
25
|
+
Requires-Dist: fastapi>=0.60.1
|
|
26
|
+
Dynamic: description
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
Dynamic: summary
|
|
24
29
|
|
|
25
30
|
This is the developmental release for Dapr FastAPI extension.
|
|
26
|
-
|
|
@@ -10,4 +10,6 @@ dapr_ext_fastapi_dev.egg-info/PKG-INFO
|
|
|
10
10
|
dapr_ext_fastapi_dev.egg-info/SOURCES.txt
|
|
11
11
|
dapr_ext_fastapi_dev.egg-info/dependency_links.txt
|
|
12
12
|
dapr_ext_fastapi_dev.egg-info/requires.txt
|
|
13
|
-
dapr_ext_fastapi_dev.egg-info/top_level.txt
|
|
13
|
+
dapr_ext_fastapi_dev.egg-info/top_level.txt
|
|
14
|
+
tests/test_app.py
|
|
15
|
+
tests/test_dapractor.py
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from dapr.ext.fastapi import DaprApp
|
|
4
|
+
from fastapi import FastAPI
|
|
5
|
+
from fastapi.testclient import TestClient
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Message(BaseModel):
|
|
10
|
+
body: str
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DaprAppTest(unittest.TestCase):
|
|
14
|
+
def setUp(self):
|
|
15
|
+
self.app = FastAPI()
|
|
16
|
+
self.dapr_app = DaprApp(self.app)
|
|
17
|
+
self.client = TestClient(self.app)
|
|
18
|
+
|
|
19
|
+
def test_subscribe_subscription_registered(self):
|
|
20
|
+
@self.dapr_app.subscribe(pubsub='pubsub', topic='test')
|
|
21
|
+
def event_handler(event_data: Message):
|
|
22
|
+
return 'default route'
|
|
23
|
+
|
|
24
|
+
self.assertEqual(len(self.dapr_app._subscriptions), 1)
|
|
25
|
+
|
|
26
|
+
self.assertIn('/dapr/subscribe', [route.path for route in self.app.router.routes])
|
|
27
|
+
self.assertIn('/events/pubsub/test', [route.path for route in self.app.router.routes])
|
|
28
|
+
|
|
29
|
+
response = self.client.get('/dapr/subscribe')
|
|
30
|
+
self.assertEqual(
|
|
31
|
+
[
|
|
32
|
+
{
|
|
33
|
+
'pubsubname': 'pubsub',
|
|
34
|
+
'topic': 'test',
|
|
35
|
+
'route': '/events/pubsub/test',
|
|
36
|
+
'metadata': {},
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
response.json(),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
response = self.client.post('/events/pubsub/test', json={'body': 'new message'})
|
|
43
|
+
self.assertEqual(response.status_code, 200)
|
|
44
|
+
self.assertEqual(response.text, '"default route"')
|
|
45
|
+
|
|
46
|
+
def test_subscribe_with_route_subscription_registered_with_custom_route(self):
|
|
47
|
+
@self.dapr_app.subscribe(pubsub='pubsub', topic='test', route='/do-something')
|
|
48
|
+
def event_handler(event_data: Message):
|
|
49
|
+
return 'custom route'
|
|
50
|
+
|
|
51
|
+
self.assertEqual(len(self.dapr_app._subscriptions), 1)
|
|
52
|
+
|
|
53
|
+
self.assertIn('/dapr/subscribe', [route.path for route in self.app.router.routes])
|
|
54
|
+
self.assertIn('/do-something', [route.path for route in self.app.router.routes])
|
|
55
|
+
|
|
56
|
+
response = self.client.get('/dapr/subscribe')
|
|
57
|
+
self.assertEqual(
|
|
58
|
+
[{'pubsubname': 'pubsub', 'topic': 'test', 'route': '/do-something', 'metadata': {}}],
|
|
59
|
+
response.json(),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
response = self.client.post('/do-something', json={'body': 'new message'})
|
|
63
|
+
self.assertEqual(response.status_code, 200)
|
|
64
|
+
self.assertEqual(response.text, '"custom route"')
|
|
65
|
+
|
|
66
|
+
def test_subscribe_metadata(self):
|
|
67
|
+
handler_metadata = {'rawPayload': 'true'}
|
|
68
|
+
|
|
69
|
+
@self.dapr_app.subscribe(pubsub='pubsub', topic='test', metadata=handler_metadata)
|
|
70
|
+
def event_handler(event_data: Message):
|
|
71
|
+
return 'custom metadata'
|
|
72
|
+
|
|
73
|
+
self.assertEqual((self.dapr_app._subscriptions[0]['metadata']['rawPayload']), 'true')
|
|
74
|
+
|
|
75
|
+
response = self.client.get('/dapr/subscribe')
|
|
76
|
+
self.assertEqual(
|
|
77
|
+
[
|
|
78
|
+
{
|
|
79
|
+
'pubsubname': 'pubsub',
|
|
80
|
+
'topic': 'test',
|
|
81
|
+
'route': '/events/pubsub/test',
|
|
82
|
+
'metadata': {'rawPayload': 'true'},
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
response.json(),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
response = self.client.post('/events/pubsub/test', json={'body': 'new message'})
|
|
89
|
+
self.assertEqual(response.status_code, 200)
|
|
90
|
+
self.assertEqual(response.text, '"custom metadata"')
|
|
91
|
+
|
|
92
|
+
def test_router_tag(self):
|
|
93
|
+
app1 = FastAPI()
|
|
94
|
+
app2 = FastAPI()
|
|
95
|
+
app3 = FastAPI()
|
|
96
|
+
DaprApp(app_instance=app1, router_tags=['MyTag', 'PubSub']).subscribe(
|
|
97
|
+
pubsub='mypubsub', topic='test'
|
|
98
|
+
)
|
|
99
|
+
DaprApp(app_instance=app2).subscribe(pubsub='mypubsub', topic='test')
|
|
100
|
+
DaprApp(app_instance=app3, router_tags=None).subscribe(pubsub='mypubsub', topic='test')
|
|
101
|
+
|
|
102
|
+
PATHS_WITH_EXPECTED_TAGS = ['/dapr/subscribe', '/events/mypubsub/test']
|
|
103
|
+
|
|
104
|
+
foundTags = False
|
|
105
|
+
for route in app1.router.routes:
|
|
106
|
+
if hasattr(route, 'tags'):
|
|
107
|
+
self.assertIn(route.path, PATHS_WITH_EXPECTED_TAGS)
|
|
108
|
+
self.assertEqual(['MyTag', 'PubSub'], route.tags)
|
|
109
|
+
foundTags = True
|
|
110
|
+
if not foundTags:
|
|
111
|
+
self.fail('No tags found')
|
|
112
|
+
|
|
113
|
+
foundTags = False
|
|
114
|
+
for route in app2.router.routes:
|
|
115
|
+
if hasattr(route, 'tags'):
|
|
116
|
+
self.assertIn(route.path, PATHS_WITH_EXPECTED_TAGS)
|
|
117
|
+
self.assertEqual(['PubSub'], route.tags)
|
|
118
|
+
foundTags = True
|
|
119
|
+
if not foundTags:
|
|
120
|
+
self.fail('No tags found')
|
|
121
|
+
|
|
122
|
+
for route in app3.router.routes:
|
|
123
|
+
if hasattr(route, 'tags'):
|
|
124
|
+
if len(route.tags) > 0:
|
|
125
|
+
self.fail('Found tags on route that should not have any')
|
|
126
|
+
|
|
127
|
+
def test_subscribe_dead_letter(self):
|
|
128
|
+
dead_letter_topic = 'dead-test'
|
|
129
|
+
|
|
130
|
+
@self.dapr_app.subscribe(pubsub='pubsub', topic='test', dead_letter_topic=dead_letter_topic)
|
|
131
|
+
def event_handler(event_data: Message):
|
|
132
|
+
return 'dead letter test'
|
|
133
|
+
|
|
134
|
+
self.assertEqual((self.dapr_app._subscriptions[0]['deadLetterTopic']), dead_letter_topic)
|
|
135
|
+
|
|
136
|
+
response = self.client.get('/dapr/subscribe')
|
|
137
|
+
self.assertEqual(
|
|
138
|
+
[
|
|
139
|
+
{
|
|
140
|
+
'pubsubname': 'pubsub',
|
|
141
|
+
'topic': 'test',
|
|
142
|
+
'route': '/events/pubsub/test',
|
|
143
|
+
'metadata': {},
|
|
144
|
+
'deadLetterTopic': dead_letter_topic,
|
|
145
|
+
}
|
|
146
|
+
],
|
|
147
|
+
response.json(),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
response = self.client.post('/events/pubsub/test', json={'body': 'new message'})
|
|
151
|
+
self.assertEqual(response.status_code, 200)
|
|
152
|
+
self.assertEqual(response.text, '"dead letter test"')
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
if __name__ == '__main__':
|
|
156
|
+
unittest.main()
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Copyright 2023 The Dapr Authors
|
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
you may not use this file except in compliance with the License.
|
|
7
|
+
You may obtain a copy of the License at
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import unittest
|
|
18
|
+
|
|
19
|
+
from dapr.ext.fastapi.actor import DaprActor, _wrap_response
|
|
20
|
+
from fastapi import FastAPI
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DaprActorTest(unittest.TestCase):
|
|
24
|
+
def test_wrap_response_str(self):
|
|
25
|
+
r = _wrap_response(200, 'fake_message')
|
|
26
|
+
self.assertEqual({'message': 'fake_message'}, json.loads(r.body))
|
|
27
|
+
self.assertEqual(200, r.status_code)
|
|
28
|
+
|
|
29
|
+
def test_wrap_response_str_err(self):
|
|
30
|
+
r = _wrap_response(400, 'fake_message', 'ERR_FAKE')
|
|
31
|
+
self.assertEqual({'message': 'fake_message', 'errorCode': 'ERR_FAKE'}, json.loads(r.body))
|
|
32
|
+
self.assertEqual(400, r.status_code)
|
|
33
|
+
|
|
34
|
+
def test_wrap_response_bytes_text(self):
|
|
35
|
+
r = _wrap_response(200, b'fake_bytes_message', content_type='text/plain')
|
|
36
|
+
self.assertEqual(b'fake_bytes_message', r.body)
|
|
37
|
+
self.assertEqual(200, r.status_code)
|
|
38
|
+
self.assertEqual('text/plain', r.media_type)
|
|
39
|
+
|
|
40
|
+
def test_wrap_response_obj(self):
|
|
41
|
+
fake_data = {'message': 'ok'}
|
|
42
|
+
r = _wrap_response(200, fake_data)
|
|
43
|
+
self.assertEqual(fake_data, json.loads(r.body))
|
|
44
|
+
self.assertEqual(200, r.status_code)
|
|
45
|
+
|
|
46
|
+
def test_router_tag(self):
|
|
47
|
+
app1 = FastAPI()
|
|
48
|
+
app2 = FastAPI()
|
|
49
|
+
app3 = FastAPI()
|
|
50
|
+
DaprActor(app=app1, router_tags=['MyTag', 'Actor'])
|
|
51
|
+
DaprActor(app=app2)
|
|
52
|
+
DaprActor(app=app3, router_tags=None)
|
|
53
|
+
|
|
54
|
+
PATHS_WITH_EXPECTED_TAGS = [
|
|
55
|
+
'/healthz',
|
|
56
|
+
'/dapr/config',
|
|
57
|
+
'/actors/{actor_type_name}/{actor_id}',
|
|
58
|
+
'/actors/{actor_type_name}/{actor_id}/method/{method_name}',
|
|
59
|
+
'/actors/{actor_type_name}/{actor_id}/method/timer/{timer_name}',
|
|
60
|
+
'/actors/{actor_type_name}/{actor_id}/method/remind/{reminder_name}',
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
foundTags = False
|
|
64
|
+
for route in app1.router.routes:
|
|
65
|
+
if hasattr(route, 'tags'):
|
|
66
|
+
self.assertIn(route.path, PATHS_WITH_EXPECTED_TAGS)
|
|
67
|
+
self.assertEqual(['MyTag', 'Actor'], route.tags)
|
|
68
|
+
foundTags = True
|
|
69
|
+
if not foundTags:
|
|
70
|
+
self.fail('No tags found')
|
|
71
|
+
|
|
72
|
+
foundTags = False
|
|
73
|
+
for route in app2.router.routes:
|
|
74
|
+
if hasattr(route, 'tags'):
|
|
75
|
+
self.assertIn(route.path, PATHS_WITH_EXPECTED_TAGS)
|
|
76
|
+
self.assertEqual(['Actor'], route.tags)
|
|
77
|
+
foundTags = True
|
|
78
|
+
if not foundTags:
|
|
79
|
+
self.fail('No tags found')
|
|
80
|
+
|
|
81
|
+
for route in app3.router.routes:
|
|
82
|
+
if hasattr(route, 'tags'):
|
|
83
|
+
if len(route.tags) > 0:
|
|
84
|
+
self.fail('Found tags on route that should not have any')
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
if __name__ == '__main__':
|
|
88
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|