ufaas 0.1.22__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.
- ufaas-0.1.22/LICENSE.txt +19 -0
- ufaas-0.1.22/PKG-INFO +71 -0
- ufaas-0.1.22/README.md +20 -0
- ufaas-0.1.22/pyproject.toml +47 -0
- ufaas-0.1.22/setup.cfg +4 -0
- ufaas-0.1.22/src/ufaas/__init__.py +3 -0
- ufaas-0.1.22/src/ufaas/apps/__init__.py +0 -0
- ufaas-0.1.22/src/ufaas/apps/base_app.py +223 -0
- ufaas-0.1.22/src/ufaas/apps/saas/__init__.py +3 -0
- ufaas-0.1.22/src/ufaas/apps/saas/saas.py +196 -0
- ufaas-0.1.22/src/ufaas/apps/saas/schemas.py +89 -0
- ufaas-0.1.22/src/ufaas/exceptions.py +36 -0
- ufaas-0.1.22/src/ufaas/fastapi/__init__.py +0 -0
- ufaas-0.1.22/src/ufaas/fastapi/integration.py +14 -0
- ufaas-0.1.22/src/ufaas/schemas.py +148 -0
- ufaas-0.1.22/src/ufaas/services.py +11 -0
- ufaas-0.1.22/src/ufaas/ufaas.py +103 -0
- ufaas-0.1.22/src/ufaas.egg-info/PKG-INFO +71 -0
- ufaas-0.1.22/src/ufaas.egg-info/SOURCES.txt +20 -0
- ufaas-0.1.22/src/ufaas.egg-info/dependency_links.txt +1 -0
- ufaas-0.1.22/src/ufaas.egg-info/requires.txt +6 -0
- ufaas-0.1.22/src/ufaas.egg-info/top_level.txt +1 -0
ufaas-0.1.22/LICENSE.txt
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2016 The Python Packaging Authority (PyPA)
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
5
|
+
the Software without restriction, including without limitation the rights to
|
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
8
|
+
so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
ufaas-0.1.22/PKG-INFO
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: ufaas
|
|
3
|
+
Version: 0.1.22
|
|
4
|
+
Summary: A client for UFaaS.
|
|
5
|
+
Author-email: Mahdi Kiani <mahdikiany@gmail.com>
|
|
6
|
+
Maintainer-email: Mahdi Kiani <mahdikiany@gmail.com>
|
|
7
|
+
License: Copyright (c) 2016 The Python Packaging Authority (PyPA)
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
10
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
11
|
+
the Software without restriction, including without limitation the rights to
|
|
12
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
13
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
14
|
+
so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
|
|
27
|
+
Project-URL: Homepage, https://github.com/ufilesorg/ufiles-python
|
|
28
|
+
Project-URL: Bug Reports, https://github.com/ufilesorg/ufiles-python/issues
|
|
29
|
+
Project-URL: Funding, https://github.com/ufilesorg/ufiles-python
|
|
30
|
+
Project-URL: Say Thanks!, https://saythanks.io/to/mahdikiani
|
|
31
|
+
Project-URL: Source, https://github.com/ufilesorg/ufiles-python
|
|
32
|
+
Keywords: ufaas,saas,usso,finance,pricing,pay as you go
|
|
33
|
+
Classifier: Development Status :: 3 - Alpha
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
36
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
37
|
+
Classifier: Programming Language :: Python :: 3
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
41
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
42
|
+
Requires-Python: >=3.9
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
License-File: LICENSE.txt
|
|
45
|
+
Requires-Dist: pydantic>=2
|
|
46
|
+
Requires-Dist: pyjwt[crypto]
|
|
47
|
+
Requires-Dist: usso>=0.27.2
|
|
48
|
+
Requires-Dist: fastapi_mongo_base
|
|
49
|
+
Requires-Dist: singleton_package
|
|
50
|
+
Requires-Dist: json-advanced
|
|
51
|
+
|
|
52
|
+
# UFiles-Client
|
|
53
|
+
|
|
54
|
+
The Ufiles-Client is a SDK for ufiles servers.
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
Install the ufiles client using pip:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install ufiles
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
Follow the quick start guides in the documentation to integrate ufiles in your application.
|
|
66
|
+
|
|
67
|
+
## Contributing
|
|
68
|
+
Contributions are welcome! See CONTRIBUTING.md for more details on how to get involved.
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
Distributed under the MIT License. See LICENSE for more information.
|
ufaas-0.1.22/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# UFiles-Client
|
|
2
|
+
|
|
3
|
+
The Ufiles-Client is a SDK for ufiles servers.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Install the ufiles client using pip:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install ufiles
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
Follow the quick start guides in the documentation to integrate ufiles in your application.
|
|
15
|
+
|
|
16
|
+
## Contributing
|
|
17
|
+
Contributions are welcome! See CONTRIBUTING.md for more details on how to get involved.
|
|
18
|
+
|
|
19
|
+
## License
|
|
20
|
+
Distributed under the MIT License. See LICENSE for more information.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ufaas"
|
|
7
|
+
version = "0.1.22"
|
|
8
|
+
description = "A client for UFaaS."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {file = "LICENSE.txt"}
|
|
12
|
+
keywords = ["ufaas", "saas", "usso", "finance", "pricing", "pay as you go"]
|
|
13
|
+
authors = [
|
|
14
|
+
{name = "Mahdi Kiani", email = "mahdikiany@gmail.com"}
|
|
15
|
+
]
|
|
16
|
+
maintainers = [
|
|
17
|
+
{name = "Mahdi Kiani", email = "mahdikiany@gmail.com"}
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 3 - Alpha",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Topic :: Software Development :: Build Tools",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3.10",
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
29
|
+
]
|
|
30
|
+
dependencies = [
|
|
31
|
+
"pydantic>=2",
|
|
32
|
+
"pyjwt[crypto]",
|
|
33
|
+
"usso>=0.27.2",
|
|
34
|
+
"fastapi_mongo_base",
|
|
35
|
+
"singleton_package",
|
|
36
|
+
"json-advanced"
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
"Homepage" = "https://github.com/ufilesorg/ufiles-python"
|
|
41
|
+
"Bug Reports" = "https://github.com/ufilesorg/ufiles-python/issues"
|
|
42
|
+
"Funding" = "https://github.com/ufilesorg/ufiles-python"
|
|
43
|
+
"Say Thanks!" = "https://saythanks.io/to/mahdikiani"
|
|
44
|
+
"Source" = "https://github.com/ufilesorg/ufiles-python"
|
|
45
|
+
|
|
46
|
+
[tool.setuptools]
|
|
47
|
+
package-data = {"sample" = ["*.dat"]}
|
ufaas-0.1.22/setup.cfg
ADDED
|
File without changes
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import singleton
|
|
4
|
+
from fastapi_mongo_base.schemas import PaginatedResponse
|
|
5
|
+
from usso.session import AsyncUssoSession, UssoSession
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class App(UssoSession):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
app_name: str = "saas",
|
|
12
|
+
*,
|
|
13
|
+
ufaas_base_url: str = os.getenv("UFAAS_URL"),
|
|
14
|
+
usso_base_url: str | None = os.getenv("USSO_URL"),
|
|
15
|
+
api_key: str | None = os.getenv("UFAAS_API_KEY"),
|
|
16
|
+
usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
|
|
17
|
+
refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
|
|
18
|
+
client: UssoSession | None = None,
|
|
19
|
+
):
|
|
20
|
+
super().__init__(
|
|
21
|
+
usso_base_url=usso_base_url,
|
|
22
|
+
api_key=api_key,
|
|
23
|
+
usso_refresh_url=usso_refresh_url,
|
|
24
|
+
refresh_token=refresh_token,
|
|
25
|
+
client=client,
|
|
26
|
+
)
|
|
27
|
+
if not ufaas_base_url and client and hasattr(client, "ufaas_base_url"):
|
|
28
|
+
ufaas_base_url = client.ufaas_base_url
|
|
29
|
+
if ufaas_base_url.endswith("/"):
|
|
30
|
+
ufaas_base_url = ufaas_base_url[:-1]
|
|
31
|
+
if not ufaas_base_url.endswith("/api/v1/apps"):
|
|
32
|
+
ufaas_base_url = f"{ufaas_base_url}/api/v1/apps"
|
|
33
|
+
self.ufaas_base_url = ufaas_base_url
|
|
34
|
+
self.app_name = app_name
|
|
35
|
+
self.app_url = f"{ufaas_base_url}/{app_name}/"
|
|
36
|
+
self.initiate_resources()
|
|
37
|
+
|
|
38
|
+
def initiate_resources(self, **kwargs):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class AsyncApp(AsyncUssoSession):
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
app_name: str = "saas",
|
|
47
|
+
*,
|
|
48
|
+
ufaas_base_url: str = os.getenv("UFAAS_URL"),
|
|
49
|
+
usso_base_url: str | None = os.getenv("USSO_URL"),
|
|
50
|
+
api_key: str | None = os.getenv("UFAAS_API_KEY"),
|
|
51
|
+
usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
|
|
52
|
+
refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
|
|
53
|
+
client: AsyncUssoSession | None = None,
|
|
54
|
+
):
|
|
55
|
+
super().__init__(
|
|
56
|
+
usso_base_url=usso_base_url,
|
|
57
|
+
api_key=api_key,
|
|
58
|
+
usso_refresh_url=usso_refresh_url,
|
|
59
|
+
refresh_token=refresh_token,
|
|
60
|
+
client=client,
|
|
61
|
+
)
|
|
62
|
+
if not ufaas_base_url and client and hasattr(client, "ufaas_base_url"):
|
|
63
|
+
ufaas_base_url = client.ufaas_base_url
|
|
64
|
+
if ufaas_base_url.endswith("/"):
|
|
65
|
+
ufaas_base_url = ufaas_base_url[:-1]
|
|
66
|
+
if not ufaas_base_url.endswith("/api/v1/apps"):
|
|
67
|
+
ufaas_base_url = f"{ufaas_base_url}/api/v1/apps"
|
|
68
|
+
self.ufaas_base_url = ufaas_base_url
|
|
69
|
+
self.app_name = app_name
|
|
70
|
+
self.app_url = f"{ufaas_base_url}/{app_name}/"
|
|
71
|
+
self.initiate_resources()
|
|
72
|
+
|
|
73
|
+
def initiate_resources(self, **kwargs):
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class Resource(UssoSession, metaclass=singleton.Singleton):
|
|
78
|
+
def __init__(
|
|
79
|
+
self,
|
|
80
|
+
app_name: str = "saas",
|
|
81
|
+
resource_name: str = "usages",
|
|
82
|
+
schema: type = dict,
|
|
83
|
+
*,
|
|
84
|
+
ufaas_base_url: str = os.getenv("UFAAS_URL"),
|
|
85
|
+
usso_base_url: str | None = os.getenv("USSO_URL"),
|
|
86
|
+
api_key: str | None = os.getenv("UFAAS_API_KEY"),
|
|
87
|
+
usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
|
|
88
|
+
refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
|
|
89
|
+
client: UssoSession | None = None,
|
|
90
|
+
):
|
|
91
|
+
super().__init__(
|
|
92
|
+
usso_base_url=usso_base_url,
|
|
93
|
+
api_key=api_key,
|
|
94
|
+
usso_refresh_url=usso_refresh_url,
|
|
95
|
+
refresh_token=refresh_token,
|
|
96
|
+
client=client,
|
|
97
|
+
)
|
|
98
|
+
if not ufaas_base_url and client and hasattr(client, "ufaas_base_url"):
|
|
99
|
+
ufaas_base_url = client.ufaas_base_url
|
|
100
|
+
if ufaas_base_url.endswith("/"):
|
|
101
|
+
ufaas_base_url = ufaas_base_url[:-1]
|
|
102
|
+
if not ufaas_base_url.endswith("/api/v1/apps"):
|
|
103
|
+
ufaas_base_url = f"{ufaas_base_url}/api/v1/apps"
|
|
104
|
+
self.ufaas_base_url = ufaas_base_url
|
|
105
|
+
self.app_name = app_name
|
|
106
|
+
self.resource_name = resource_name
|
|
107
|
+
self.app_url = f"{ufaas_base_url}/{app_name}/"
|
|
108
|
+
self.resource_url = f"{self.app_url}{resource_name}/"
|
|
109
|
+
self._config_schemas(schema)
|
|
110
|
+
|
|
111
|
+
def _config_schemas(self, schema: type = dict, **kwargs):
|
|
112
|
+
self.schema = schema
|
|
113
|
+
if schema == dict:
|
|
114
|
+
self.list_response_schema = dict
|
|
115
|
+
else:
|
|
116
|
+
self.list_response_schema = PaginatedResponse[schema]
|
|
117
|
+
self.list_item_schema = schema
|
|
118
|
+
self.retrieve_response_schema = schema
|
|
119
|
+
self.create_response_schema = schema
|
|
120
|
+
self.update_response_schema = schema
|
|
121
|
+
self.delete_response_schema = schema
|
|
122
|
+
|
|
123
|
+
def list_items(self, offset: int = 0, limit: int = 10, **kwargs):
|
|
124
|
+
resp = self.get(
|
|
125
|
+
self.resource_url, params={"offset": offset, "limit": limit, **kwargs}
|
|
126
|
+
)
|
|
127
|
+
resp.raise_for_status()
|
|
128
|
+
return self.list_response_schema(**resp.json())
|
|
129
|
+
|
|
130
|
+
def retrieve_item(self, uid: str, **kwargs):
|
|
131
|
+
resp = self.get(f"{self.resource_url}/{uid}", params=kwargs)
|
|
132
|
+
resp.raise_for_status()
|
|
133
|
+
return self.retrieve_response_schema(**resp.json())
|
|
134
|
+
|
|
135
|
+
def create_item(self, obj: dict, **kwargs):
|
|
136
|
+
resp = self.post(self.resource_url, json=obj, params=kwargs)
|
|
137
|
+
resp.raise_for_status()
|
|
138
|
+
return self.create_response_schema(**resp.json())
|
|
139
|
+
|
|
140
|
+
def update_item(self, uid: str, obj: dict, **kwargs):
|
|
141
|
+
resp = self.patch(f"{self.resource_url}/{uid}", json=obj, params=kwargs)
|
|
142
|
+
resp.raise_for_status()
|
|
143
|
+
return self.update_response_schema(**resp.json())
|
|
144
|
+
|
|
145
|
+
def delete_item(self, uid: str, **kwargs):
|
|
146
|
+
resp = self.delete(f"{self.resource_url}/{uid}", params=kwargs)
|
|
147
|
+
resp.raise_for_status()
|
|
148
|
+
return self.delete_response_schema(**resp.json())
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class AsyncResource(AsyncUssoSession, metaclass=singleton.Singleton):
|
|
152
|
+
|
|
153
|
+
def __init__(
|
|
154
|
+
self,
|
|
155
|
+
app_name: str = "saas",
|
|
156
|
+
resource_name: str = "usages",
|
|
157
|
+
schema: type = dict,
|
|
158
|
+
*,
|
|
159
|
+
ufaas_base_url: str = os.getenv("UFAAS_URL"),
|
|
160
|
+
usso_base_url: str | None = os.getenv("USSO_URL"),
|
|
161
|
+
api_key: str | None = os.getenv("UFAAS_API_KEY"),
|
|
162
|
+
usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
|
|
163
|
+
refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
|
|
164
|
+
client: AsyncUssoSession | None = None,
|
|
165
|
+
):
|
|
166
|
+
super().__init__(
|
|
167
|
+
usso_base_url=usso_base_url,
|
|
168
|
+
api_key=api_key,
|
|
169
|
+
usso_refresh_url=usso_refresh_url,
|
|
170
|
+
refresh_token=refresh_token,
|
|
171
|
+
client=client,
|
|
172
|
+
)
|
|
173
|
+
if not ufaas_base_url and client and hasattr(client, "ufaas_base_url"):
|
|
174
|
+
ufaas_base_url = client.ufaas_base_url
|
|
175
|
+
if ufaas_base_url.endswith("/"):
|
|
176
|
+
ufaas_base_url = ufaas_base_url[:-1]
|
|
177
|
+
if not ufaas_base_url.endswith("/api/v1/apps"):
|
|
178
|
+
ufaas_base_url = f"{ufaas_base_url}/api/v1/apps"
|
|
179
|
+
self.ufaas_base_url = ufaas_base_url
|
|
180
|
+
self.app_name = app_name
|
|
181
|
+
self.resource_name = resource_name
|
|
182
|
+
self.app_url = f"{ufaas_base_url}/{app_name}/"
|
|
183
|
+
self.resource_url = f"{self.app_url}{resource_name}/"
|
|
184
|
+
self._config_schemas(schema)
|
|
185
|
+
|
|
186
|
+
def _config_schemas(self, schema: type = dict, **kwargs):
|
|
187
|
+
self.schema = schema
|
|
188
|
+
if schema == dict:
|
|
189
|
+
self.list_response_schema = dict
|
|
190
|
+
else:
|
|
191
|
+
self.list_response_schema = PaginatedResponse[schema]
|
|
192
|
+
self.list_item_schema = schema
|
|
193
|
+
self.retrieve_response_schema = schema
|
|
194
|
+
self.create_response_schema = schema
|
|
195
|
+
self.update_response_schema = schema
|
|
196
|
+
self.delete_response_schema = schema
|
|
197
|
+
|
|
198
|
+
async def list_items(self, offset: int = 0, limit: int = 10, **kwargs):
|
|
199
|
+
resp = await self.get(
|
|
200
|
+
self.resource_url, params={"offset": offset, "limit": limit, **kwargs}
|
|
201
|
+
)
|
|
202
|
+
resp.raise_for_status()
|
|
203
|
+
return self.list_response_schema(**resp.json())
|
|
204
|
+
|
|
205
|
+
async def retrieve_item(self, uid: str, **kwargs):
|
|
206
|
+
resp = await self.get(f"{self.resource_url}/{uid}", params=kwargs)
|
|
207
|
+
resp.raise_for_status()
|
|
208
|
+
return self.retrieve_response_schema(**resp.json())
|
|
209
|
+
|
|
210
|
+
async def create_item(self, obj: dict, **kwargs):
|
|
211
|
+
resp = await self.post(self.resource_url, json=obj, params=kwargs)
|
|
212
|
+
resp.raise_for_status()
|
|
213
|
+
return self.create_response_schema(**resp.json())
|
|
214
|
+
|
|
215
|
+
async def update_item(self, uid: str, obj: dict, **kwargs):
|
|
216
|
+
resp = await self.patch(f"{self.resource_url}/{uid}", json=obj, params=kwargs)
|
|
217
|
+
resp.raise_for_status()
|
|
218
|
+
return self.update_response_schema(**resp.json())
|
|
219
|
+
|
|
220
|
+
async def delete_item(self, uid: str, **kwargs):
|
|
221
|
+
resp = await self.delete(f"{self.resource_url}/{uid}", params=kwargs)
|
|
222
|
+
resp.raise_for_status()
|
|
223
|
+
return self.delete_response_schema(**resp.json())
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import uuid
|
|
3
|
+
|
|
4
|
+
from usso.session import AsyncUssoSession, UssoSession
|
|
5
|
+
|
|
6
|
+
from ..base_app import App, AsyncApp, AsyncResource, Resource
|
|
7
|
+
from .schemas import EnrollmentSchema, QuotaSchema, UsageSchema
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SaaS(App):
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
*,
|
|
14
|
+
ufaas_base_url: str = os.getenv("UFAAS_URL"),
|
|
15
|
+
usso_base_url: str | None = os.getenv("USSO_URL"),
|
|
16
|
+
api_key: str | None = os.getenv("UFAAS_API_KEY"),
|
|
17
|
+
usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
|
|
18
|
+
refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
|
|
19
|
+
client: UssoSession | None = None,
|
|
20
|
+
):
|
|
21
|
+
super().__init__(
|
|
22
|
+
app_name="saas",
|
|
23
|
+
ufaas_base_url=ufaas_base_url,
|
|
24
|
+
usso_base_url=usso_base_url,
|
|
25
|
+
api_key=api_key,
|
|
26
|
+
usso_refresh_url=usso_refresh_url,
|
|
27
|
+
refresh_token=refresh_token,
|
|
28
|
+
client=client,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def initiate_resources(self, **kwargs):
|
|
32
|
+
self.usages = Usage(client=self)
|
|
33
|
+
self.enrollments = Enrollment(client=self)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AsyncSaaS(AsyncApp):
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
*,
|
|
41
|
+
ufaas_base_url: str = os.getenv("UFAAS_URL"),
|
|
42
|
+
usso_base_url: str | None = os.getenv("USSO_URL"),
|
|
43
|
+
api_key: str | None = os.getenv("UFAAS_API_KEY"),
|
|
44
|
+
usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
|
|
45
|
+
refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
|
|
46
|
+
client: AsyncUssoSession | None = None,
|
|
47
|
+
):
|
|
48
|
+
super().__init__(
|
|
49
|
+
app_name="saas",
|
|
50
|
+
ufaas_base_url=ufaas_base_url,
|
|
51
|
+
usso_base_url=usso_base_url,
|
|
52
|
+
api_key=api_key,
|
|
53
|
+
usso_refresh_url=usso_refresh_url,
|
|
54
|
+
refresh_token=refresh_token,
|
|
55
|
+
client=client,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def initiate_resources(self, **kwargs):
|
|
59
|
+
self.usages = AsyncUsage(client=self)
|
|
60
|
+
self.enrollments = AsyncEnrollment(client=self)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class Usage(Resource):
|
|
64
|
+
|
|
65
|
+
def __init__(
|
|
66
|
+
self,
|
|
67
|
+
*,
|
|
68
|
+
ufaas_base_url: str = os.getenv("UFAAS_URL"),
|
|
69
|
+
usso_base_url: str | None = os.getenv("USSO_URL"),
|
|
70
|
+
api_key: str | None = os.getenv("UFAAS_API_KEY"),
|
|
71
|
+
usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
|
|
72
|
+
refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
|
|
73
|
+
client: UssoSession | None = None,
|
|
74
|
+
):
|
|
75
|
+
super().__init__(
|
|
76
|
+
app_name="saas",
|
|
77
|
+
resource_name="usages",
|
|
78
|
+
schema=UsageSchema,
|
|
79
|
+
ufaas_base_url=ufaas_base_url,
|
|
80
|
+
usso_base_url=usso_base_url,
|
|
81
|
+
api_key=api_key,
|
|
82
|
+
usso_refresh_url=usso_refresh_url,
|
|
83
|
+
refresh_token=refresh_token,
|
|
84
|
+
client=client,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def cancel_item(self, uid: str, **kwargs):
|
|
88
|
+
resp = self.post(f"{self.resource_url}{uid}/cancel", params=kwargs)
|
|
89
|
+
resp.raise_for_status()
|
|
90
|
+
return self.retrieve_response_schema(**resp.json())
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class AsyncUsage(AsyncResource):
|
|
94
|
+
|
|
95
|
+
def __init__(
|
|
96
|
+
self,
|
|
97
|
+
*,
|
|
98
|
+
ufaas_base_url: str = os.getenv("UFAAS_URL"),
|
|
99
|
+
usso_base_url: str | None = os.getenv("USSO_URL"),
|
|
100
|
+
api_key: str | None = os.getenv("UFAAS_API_KEY"),
|
|
101
|
+
usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
|
|
102
|
+
refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
|
|
103
|
+
client: AsyncUssoSession | None = None,
|
|
104
|
+
):
|
|
105
|
+
super().__init__(
|
|
106
|
+
app_name="saas",
|
|
107
|
+
resource_name="usages",
|
|
108
|
+
schema=UsageSchema,
|
|
109
|
+
ufaas_base_url=ufaas_base_url,
|
|
110
|
+
usso_base_url=usso_base_url,
|
|
111
|
+
api_key=api_key,
|
|
112
|
+
usso_refresh_url=usso_refresh_url,
|
|
113
|
+
refresh_token=refresh_token,
|
|
114
|
+
client=client,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
async def cancel_item(self, uid: str, **kwargs):
|
|
118
|
+
resp = await self.post(f"{self.resource_url}{uid}/cancel", params=kwargs)
|
|
119
|
+
resp.raise_for_status()
|
|
120
|
+
return self.retrieve_response_schema(**resp.json())
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class Enrollment(Resource):
|
|
124
|
+
def __init__(
|
|
125
|
+
self,
|
|
126
|
+
*,
|
|
127
|
+
ufaas_base_url: str = os.getenv("UFAAS_URL"),
|
|
128
|
+
usso_base_url: str | None = os.getenv("USSO_URL"),
|
|
129
|
+
api_key: str | None = os.getenv("UFAAS_API_KEY"),
|
|
130
|
+
usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
|
|
131
|
+
refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
|
|
132
|
+
client: UssoSession | None = None,
|
|
133
|
+
):
|
|
134
|
+
super().__init__(
|
|
135
|
+
app_name="saas",
|
|
136
|
+
resource_name="enrollments",
|
|
137
|
+
schema=EnrollmentSchema,
|
|
138
|
+
ufaas_base_url=ufaas_base_url,
|
|
139
|
+
usso_base_url=usso_base_url,
|
|
140
|
+
api_key=api_key,
|
|
141
|
+
usso_refresh_url=usso_refresh_url,
|
|
142
|
+
refresh_token=refresh_token,
|
|
143
|
+
client=client,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
def get_quotas(
|
|
147
|
+
self,
|
|
148
|
+
asset: str,
|
|
149
|
+
user_id: uuid.UUID | None = None,
|
|
150
|
+
variant: str | None = None,
|
|
151
|
+
**kwargs,
|
|
152
|
+
):
|
|
153
|
+
if isinstance(user_id, uuid.UUID):
|
|
154
|
+
user_id = str(user_id)
|
|
155
|
+
params = {"asset": asset, "user_id": user_id, "variant": variant, **kwargs}
|
|
156
|
+
resp = self.get(f"{self.resource_url}quotas", params=params)
|
|
157
|
+
resp.raise_for_status()
|
|
158
|
+
return QuotaSchema(**resp.json())
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class AsyncEnrollment(AsyncResource):
|
|
162
|
+
def __init__(
|
|
163
|
+
self,
|
|
164
|
+
*,
|
|
165
|
+
ufaas_base_url: str = os.getenv("UFAAS_URL"),
|
|
166
|
+
usso_base_url: str | None = os.getenv("USSO_URL"),
|
|
167
|
+
api_key: str | None = os.getenv("UFAAS_API_KEY"),
|
|
168
|
+
usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
|
|
169
|
+
refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
|
|
170
|
+
client: AsyncUssoSession | None = None,
|
|
171
|
+
):
|
|
172
|
+
super().__init__(
|
|
173
|
+
app_name="saas",
|
|
174
|
+
resource_name="enrollments",
|
|
175
|
+
schema=EnrollmentSchema,
|
|
176
|
+
ufaas_base_url=ufaas_base_url,
|
|
177
|
+
usso_base_url=usso_base_url,
|
|
178
|
+
api_key=api_key,
|
|
179
|
+
usso_refresh_url=usso_refresh_url,
|
|
180
|
+
refresh_token=refresh_token,
|
|
181
|
+
client=client,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
async def get_quotas(
|
|
185
|
+
self,
|
|
186
|
+
asset: str,
|
|
187
|
+
user_id: uuid.UUID | None = None,
|
|
188
|
+
variant: str | None = None,
|
|
189
|
+
**kwargs,
|
|
190
|
+
):
|
|
191
|
+
if isinstance(user_id, uuid.UUID):
|
|
192
|
+
user_id = str(user_id)
|
|
193
|
+
params = {"asset": asset, "user_id": user_id, "variant": variant, **kwargs}
|
|
194
|
+
resp = await self.get(f"{self.resource_url}quotas", params=params)
|
|
195
|
+
resp.raise_for_status()
|
|
196
|
+
return QuotaSchema(**resp.json())
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
from fastapi_mongo_base.schemas import BusinessOwnedEntitySchema
|
|
8
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Bundle(BaseModel):
|
|
12
|
+
asset: str
|
|
13
|
+
quota: Decimal
|
|
14
|
+
unit: str | None = None
|
|
15
|
+
|
|
16
|
+
model_config = ConfigDict(allow_inf_nan=True)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AcquisitionType(str, Enum):
|
|
20
|
+
trial = "trial"
|
|
21
|
+
# credit = "credit"
|
|
22
|
+
purchased = "purchased"
|
|
23
|
+
gifted = "gifted"
|
|
24
|
+
# deferred = "deferred"
|
|
25
|
+
promotion = "promotion"
|
|
26
|
+
# subscription = "subscription"
|
|
27
|
+
# on_demand = "on_demand"
|
|
28
|
+
borrowed = "borrowed"
|
|
29
|
+
# freemium = "freemium"
|
|
30
|
+
postpaid = "postpaid"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class EnrollmentCreateSchema(BaseModel):
|
|
34
|
+
user_id: uuid.UUID
|
|
35
|
+
bundles: list[Bundle]
|
|
36
|
+
|
|
37
|
+
price: Decimal = Decimal(0)
|
|
38
|
+
invoice_id: str | None = None
|
|
39
|
+
start_at: datetime = Field(default_factory=datetime.now)
|
|
40
|
+
expire_at: datetime | None = None
|
|
41
|
+
duration: int | None = Field(None, description="Duration in days")
|
|
42
|
+
status: Literal["active", "inactive"] = "active"
|
|
43
|
+
acquisition_type: AcquisitionType = AcquisitionType.purchased
|
|
44
|
+
|
|
45
|
+
variant: str | None = None
|
|
46
|
+
meta_data: dict | None = None
|
|
47
|
+
|
|
48
|
+
due_date: datetime | None = None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class EnrollmentSchema(EnrollmentCreateSchema, BusinessOwnedEntitySchema):
|
|
52
|
+
paid_at: datetime | None = None
|
|
53
|
+
leftover_bundles: list[Bundle] = []
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class QuotaSchema(BaseModel):
|
|
57
|
+
asset: str
|
|
58
|
+
quota: Decimal
|
|
59
|
+
unit: str | None = None
|
|
60
|
+
variant: str | None = None
|
|
61
|
+
_quota: Decimal | None = None
|
|
62
|
+
|
|
63
|
+
model_config = ConfigDict(allow_inf_nan=True)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class UsageCreateSchema(BaseModel):
|
|
67
|
+
user_id: uuid.UUID
|
|
68
|
+
enrollment_id: uuid.UUID | None = None
|
|
69
|
+
asset: str
|
|
70
|
+
amount: Decimal = Decimal(1)
|
|
71
|
+
variant: str | None = None
|
|
72
|
+
meta_data: dict | None = None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class UsageConsumption(BaseModel):
|
|
76
|
+
enrollment_id: uuid.UUID
|
|
77
|
+
amount: Decimal
|
|
78
|
+
leftover_bundles: list[Bundle] = []
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class UsageSchema(BusinessOwnedEntitySchema):
|
|
82
|
+
# enrollment_id: uuid.UUID
|
|
83
|
+
# asset: str
|
|
84
|
+
# amount: Decimal
|
|
85
|
+
|
|
86
|
+
consumptions: list[UsageConsumption]
|
|
87
|
+
asset: str
|
|
88
|
+
amount: Decimal
|
|
89
|
+
variant: str | None = None
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
error_messages = {}
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class UFaaSException(Exception):
|
|
5
|
+
def __init__(self, status_code: int, error: str, message: str = None):
|
|
6
|
+
self.status_code = status_code
|
|
7
|
+
self.error = error
|
|
8
|
+
self.message = message
|
|
9
|
+
if message is None:
|
|
10
|
+
self.message = error_messages[error]
|
|
11
|
+
super().__init__(message)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class InsufficientFunds(UFaaSException):
|
|
15
|
+
def __init__(self, message: str = None):
|
|
16
|
+
super().__init__(status_code=402, error="insufficient_funds", message=message)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class InvalidRequest(UFaaSException):
|
|
20
|
+
def __init__(self, message: str = None):
|
|
21
|
+
super().__init__(status_code=400, error="invalid_request", message=message)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Unauthorized(UFaaSException):
|
|
25
|
+
def __init__(self, message: str = None):
|
|
26
|
+
super().__init__(status_code=401, error="unauthorized", message=message)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Forbidden(UFaaSException):
|
|
30
|
+
def __init__(self, message: str = None):
|
|
31
|
+
super().__init__(status_code=403, error="forbidden", message=message)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class NotFound(UFaaSException):
|
|
35
|
+
def __init__(self, message: str = None):
|
|
36
|
+
super().__init__(status_code=404, error="not_found", message=message)
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from fastapi import Request
|
|
2
|
+
from fastapi.responses import JSONResponse
|
|
3
|
+
|
|
4
|
+
from ..exceptions import UFaaSException
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
async def ufaas_exception_handler(request: Request, exc: UFaaSException):
|
|
8
|
+
return JSONResponse(
|
|
9
|
+
status_code=exc.status_code,
|
|
10
|
+
content={"message": exc.message, "error": exc.error},
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
EXCEPTION_HANDLERS = {UFaaSException: ufaas_exception_handler}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
from fastapi_mongo_base.schemas import BusinessOwnedEntitySchema
|
|
8
|
+
from pydantic import BaseModel, ConfigDict, field_serializer, model_validator
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Currency(str, Enum):
|
|
12
|
+
none = "none"
|
|
13
|
+
|
|
14
|
+
IRR = "IRR"
|
|
15
|
+
IRT = "IRT"
|
|
16
|
+
|
|
17
|
+
USD = "USD"
|
|
18
|
+
EUR = "EUR"
|
|
19
|
+
GBP = "GBP"
|
|
20
|
+
|
|
21
|
+
USDT = "USDT"
|
|
22
|
+
BTC = "BTC"
|
|
23
|
+
ETH = "ETH"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class WalletType(str, Enum):
|
|
27
|
+
user = "user"
|
|
28
|
+
business = "business"
|
|
29
|
+
app = "app"
|
|
30
|
+
app_operational = "app_operational"
|
|
31
|
+
app_income = "app_income"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class WalletSchema(BusinessOwnedEntitySchema):
|
|
35
|
+
wallet_type: WalletType = WalletType.user
|
|
36
|
+
main_currency: Currency = Currency.none
|
|
37
|
+
|
|
38
|
+
is_default: bool = True
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class WalletDetailSchema(BusinessOwnedEntitySchema):
|
|
42
|
+
balance: dict[str, Decimal] = {}
|
|
43
|
+
|
|
44
|
+
model_config = ConfigDict(allow_inf_nan=True)
|
|
45
|
+
|
|
46
|
+
@field_serializer("balance")
|
|
47
|
+
def serialize_balance(self, balance: dict[str, Decimal]) -> dict[str, Decimal]:
|
|
48
|
+
return {k: (v if v.is_finite() else Decimal(0)) for k, v in balance.items()}
|
|
49
|
+
|
|
50
|
+
@field_serializer("wallet_type")
|
|
51
|
+
def serialize_wallet_type(self, wallet_type: WalletType) -> str:
|
|
52
|
+
return wallet_type.value
|
|
53
|
+
|
|
54
|
+
@field_serializer("main_currency")
|
|
55
|
+
def serialize_main_currency(self, main_currency: Currency) -> str:
|
|
56
|
+
return main_currency.value
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class WalletCreateSchema(BaseModel):
|
|
60
|
+
user_id: uuid.UUID
|
|
61
|
+
meta_data: dict | None = None
|
|
62
|
+
|
|
63
|
+
wallet_type: WalletType = WalletType.user
|
|
64
|
+
main_currency: Currency = Currency.none
|
|
65
|
+
|
|
66
|
+
@model_validator(mode="before")
|
|
67
|
+
def validate_wallet_type(cls, values):
|
|
68
|
+
if values.get("wallet_type") and not values.get("main_currency"):
|
|
69
|
+
raise ValueError("main_currency is required for income wallet")
|
|
70
|
+
return values
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class WalletUpdateSchema(BaseModel):
|
|
74
|
+
meta_data: dict | None = None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class WalletHoldSchema(BusinessOwnedEntitySchema):
|
|
78
|
+
wallet_id: uuid.UUID
|
|
79
|
+
currency: str
|
|
80
|
+
amount: Decimal
|
|
81
|
+
expires_at: datetime
|
|
82
|
+
status: str
|
|
83
|
+
description: str | None = None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class WalletHoldCreateSchema(BaseModel):
|
|
87
|
+
amount: Decimal
|
|
88
|
+
expires_at: datetime
|
|
89
|
+
status: str = "active"
|
|
90
|
+
meta_data: dict | None = None
|
|
91
|
+
description: str | None = None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class WalletHoldUpdateSchema(BaseModel):
|
|
95
|
+
expires_at: datetime | None = None
|
|
96
|
+
status: str | None = None
|
|
97
|
+
meta_data: dict | None = None
|
|
98
|
+
description: str | None = None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class TransactionSchema(BusinessOwnedEntitySchema):
|
|
102
|
+
proposal_id: uuid.UUID
|
|
103
|
+
wallet_id: uuid.UUID
|
|
104
|
+
amount: Decimal
|
|
105
|
+
currency: str
|
|
106
|
+
balance: Decimal
|
|
107
|
+
description: str | None = None
|
|
108
|
+
note: str | None = None
|
|
109
|
+
|
|
110
|
+
model_config = ConfigDict(allow_inf_nan=True)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class TransactionNoteUpdateSchema(BaseModel):
|
|
114
|
+
note: str
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class Participant(BaseModel):
|
|
118
|
+
wallet_id: uuid.UUID
|
|
119
|
+
amount: Decimal
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class ProposalSchema(BusinessOwnedEntitySchema):
|
|
123
|
+
issuer_id: uuid.UUID
|
|
124
|
+
amount: Decimal
|
|
125
|
+
description: str | None = None
|
|
126
|
+
note: str | None = None
|
|
127
|
+
currency: str
|
|
128
|
+
# status: str
|
|
129
|
+
task_status: str
|
|
130
|
+
participants: list[Participant]
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class ProposalCreateSchema(BaseModel):
|
|
134
|
+
amount: Decimal
|
|
135
|
+
description: str | None = None
|
|
136
|
+
note: str | None = None
|
|
137
|
+
currency: str
|
|
138
|
+
task_status: Literal["draft", "init"] = "draft"
|
|
139
|
+
participants: list[Participant]
|
|
140
|
+
meta_data: dict | None = None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class ProposalUpdateSchema(BaseModel):
|
|
144
|
+
# status: str | None
|
|
145
|
+
task_status: Literal["init"] | None = None
|
|
146
|
+
description: str | None = None
|
|
147
|
+
note: str | None = None
|
|
148
|
+
meta_data: dict | None = None
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
from io import BytesIO
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def calculate_file_hash(file: BytesIO) -> str:
|
|
6
|
+
file_hash = hashlib.md5()
|
|
7
|
+
file.seek(0) # Ensure we start from the beginning of the file
|
|
8
|
+
while chunk := file.read(8192):
|
|
9
|
+
file_hash.update(chunk)
|
|
10
|
+
file.seek(0) # Reset file pointer to the beginning
|
|
11
|
+
return file_hash.hexdigest()
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from urllib.parse import urlparse
|
|
3
|
+
|
|
4
|
+
from usso.session import AsyncUssoSession, UssoSession
|
|
5
|
+
|
|
6
|
+
from .apps.saas import AsyncSaaS, SaaS
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class UFaaS(UssoSession):
|
|
10
|
+
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
*,
|
|
14
|
+
ufaas_base_url: str = os.getenv("UFAAS_URL"),
|
|
15
|
+
usso_base_url: str | None = os.getenv("USSO_URL"),
|
|
16
|
+
api_key: str | None = os.getenv("UFAAS_API_KEY"),
|
|
17
|
+
usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
|
|
18
|
+
refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
|
|
19
|
+
client: UssoSession | None = None,
|
|
20
|
+
):
|
|
21
|
+
if usso_base_url is None:
|
|
22
|
+
# calculate sso_url using ufiles_url
|
|
23
|
+
# for example: media.pixiee.io/v1/f -> sso.pixiee.io
|
|
24
|
+
# for example: media.ufaas.io/v1/f -> sso.ufaas.io
|
|
25
|
+
# for example: media.pixy.ir/api/v1/f -> sso.pixy.ir
|
|
26
|
+
# for example: storage.pixy.ir/api/v1/f -> sso.pixy.ir
|
|
27
|
+
parsed_url = urlparse(ufaas_base_url)
|
|
28
|
+
netloc = parsed_url.netloc
|
|
29
|
+
netloc_parts = netloc.split(".")
|
|
30
|
+
if len(netloc_parts) > 2:
|
|
31
|
+
netloc_parts[0] = "sso"
|
|
32
|
+
else:
|
|
33
|
+
netloc_parts = ["sso", netloc]
|
|
34
|
+
netloc = ".".join(netloc_parts)
|
|
35
|
+
usso_base_url = f"https://{netloc}"
|
|
36
|
+
|
|
37
|
+
super().__init__(
|
|
38
|
+
usso_base_url=usso_base_url,
|
|
39
|
+
api_key=api_key,
|
|
40
|
+
usso_refresh_url=usso_refresh_url,
|
|
41
|
+
refresh_token=refresh_token,
|
|
42
|
+
client=client,
|
|
43
|
+
)
|
|
44
|
+
if not ufaas_base_url and client and hasattr(client, "ufaas_base_url"):
|
|
45
|
+
ufaas_base_url = client.ufaas_base_url
|
|
46
|
+
assert ufaas_base_url, "UFAAS_URL is required"
|
|
47
|
+
if ufaas_base_url.endswith("/"):
|
|
48
|
+
ufaas_base_url = ufaas_base_url[:-1]
|
|
49
|
+
self.ufaas_base_url = ufaas_base_url
|
|
50
|
+
self.headers.update({"accept-encoding": "identity"})
|
|
51
|
+
|
|
52
|
+
self.initiate_apps()
|
|
53
|
+
|
|
54
|
+
def initiate_apps(self):
|
|
55
|
+
self.saas = SaaS(client=self)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class AsyncUFaaS(AsyncUssoSession):
|
|
59
|
+
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
*,
|
|
63
|
+
ufaas_base_url: str = os.getenv("UFAAS_URL"),
|
|
64
|
+
usso_base_url: str | None = os.getenv("USSO_URL"),
|
|
65
|
+
api_key: str | None = os.getenv("UFILES_API_KEY"),
|
|
66
|
+
usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
|
|
67
|
+
refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
|
|
68
|
+
client: AsyncUssoSession | None = None,
|
|
69
|
+
):
|
|
70
|
+
if usso_base_url is None:
|
|
71
|
+
# calculate sso_url using ufiles_url
|
|
72
|
+
# for example: media.pixiee.io/v1/f -> sso.pixiee.io
|
|
73
|
+
# for example: media.ufaas.io/v1/f -> sso.ufaas.io
|
|
74
|
+
# for example: media.pixy.ir/api/v1/f -> sso.pixy.ir
|
|
75
|
+
# for example: storage.pixy.ir/api/v1/f -> sso.pixy.ir
|
|
76
|
+
parsed_url = urlparse(ufaas_base_url)
|
|
77
|
+
netloc = parsed_url.netloc
|
|
78
|
+
netloc_parts = netloc.split(".")
|
|
79
|
+
if len(netloc_parts) > 2:
|
|
80
|
+
netloc_parts[0] = "sso"
|
|
81
|
+
else:
|
|
82
|
+
netloc_parts = ["sso", netloc]
|
|
83
|
+
netloc = ".".join(netloc_parts)
|
|
84
|
+
usso_base_url = f"https://{netloc}"
|
|
85
|
+
|
|
86
|
+
super().__init__(
|
|
87
|
+
usso_base_url=usso_base_url,
|
|
88
|
+
api_key=api_key,
|
|
89
|
+
usso_refresh_url=usso_refresh_url,
|
|
90
|
+
refresh_token=refresh_token,
|
|
91
|
+
client=client,
|
|
92
|
+
)
|
|
93
|
+
if not ufaas_base_url and client and hasattr(client, "ufaas_base_url"):
|
|
94
|
+
ufaas_base_url = client.ufaas_base_url
|
|
95
|
+
assert ufaas_base_url, "UFAAS_URL is required"
|
|
96
|
+
if ufaas_base_url.endswith("/"):
|
|
97
|
+
ufaas_base_url = ufaas_base_url[:-1]
|
|
98
|
+
self.ufaas_base_url = ufaas_base_url
|
|
99
|
+
self.headers.update({"accept-encoding": "identity"})
|
|
100
|
+
self.initiate_apps()
|
|
101
|
+
|
|
102
|
+
def initiate_apps(self):
|
|
103
|
+
self.saas = AsyncSaaS(client=self)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: ufaas
|
|
3
|
+
Version: 0.1.22
|
|
4
|
+
Summary: A client for UFaaS.
|
|
5
|
+
Author-email: Mahdi Kiani <mahdikiany@gmail.com>
|
|
6
|
+
Maintainer-email: Mahdi Kiani <mahdikiany@gmail.com>
|
|
7
|
+
License: Copyright (c) 2016 The Python Packaging Authority (PyPA)
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
10
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
11
|
+
the Software without restriction, including without limitation the rights to
|
|
12
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
13
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
14
|
+
so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
|
|
27
|
+
Project-URL: Homepage, https://github.com/ufilesorg/ufiles-python
|
|
28
|
+
Project-URL: Bug Reports, https://github.com/ufilesorg/ufiles-python/issues
|
|
29
|
+
Project-URL: Funding, https://github.com/ufilesorg/ufiles-python
|
|
30
|
+
Project-URL: Say Thanks!, https://saythanks.io/to/mahdikiani
|
|
31
|
+
Project-URL: Source, https://github.com/ufilesorg/ufiles-python
|
|
32
|
+
Keywords: ufaas,saas,usso,finance,pricing,pay as you go
|
|
33
|
+
Classifier: Development Status :: 3 - Alpha
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
36
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
37
|
+
Classifier: Programming Language :: Python :: 3
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
41
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
42
|
+
Requires-Python: >=3.9
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
License-File: LICENSE.txt
|
|
45
|
+
Requires-Dist: pydantic>=2
|
|
46
|
+
Requires-Dist: pyjwt[crypto]
|
|
47
|
+
Requires-Dist: usso>=0.27.2
|
|
48
|
+
Requires-Dist: fastapi_mongo_base
|
|
49
|
+
Requires-Dist: singleton_package
|
|
50
|
+
Requires-Dist: json-advanced
|
|
51
|
+
|
|
52
|
+
# UFiles-Client
|
|
53
|
+
|
|
54
|
+
The Ufiles-Client is a SDK for ufiles servers.
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
Install the ufiles client using pip:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install ufiles
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
Follow the quick start guides in the documentation to integrate ufiles in your application.
|
|
66
|
+
|
|
67
|
+
## Contributing
|
|
68
|
+
Contributions are welcome! See CONTRIBUTING.md for more details on how to get involved.
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
Distributed under the MIT License. See LICENSE for more information.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
LICENSE.txt
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/ufaas/__init__.py
|
|
5
|
+
src/ufaas/exceptions.py
|
|
6
|
+
src/ufaas/schemas.py
|
|
7
|
+
src/ufaas/services.py
|
|
8
|
+
src/ufaas/ufaas.py
|
|
9
|
+
src/ufaas.egg-info/PKG-INFO
|
|
10
|
+
src/ufaas.egg-info/SOURCES.txt
|
|
11
|
+
src/ufaas.egg-info/dependency_links.txt
|
|
12
|
+
src/ufaas.egg-info/requires.txt
|
|
13
|
+
src/ufaas.egg-info/top_level.txt
|
|
14
|
+
src/ufaas/apps/__init__.py
|
|
15
|
+
src/ufaas/apps/base_app.py
|
|
16
|
+
src/ufaas/apps/saas/__init__.py
|
|
17
|
+
src/ufaas/apps/saas/saas.py
|
|
18
|
+
src/ufaas/apps/saas/schemas.py
|
|
19
|
+
src/ufaas/fastapi/__init__.py
|
|
20
|
+
src/ufaas/fastapi/integration.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ufaas
|