ufaas 0.1.0__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.
@@ -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.0/PKG-INFO ADDED
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.1
2
+ Name: ufaas
3
+ Version: 0.1.0
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
+
50
+ # UFiles-Client
51
+
52
+ The Ufiles-Client is a SDK for ufiles servers.
53
+
54
+ ## Installation
55
+
56
+ Install the ufiles client using pip:
57
+
58
+ ```bash
59
+ pip install ufiles
60
+ ```
61
+
62
+ ## Quick Start
63
+ Follow the quick start guides in the documentation to integrate ufiles in your application.
64
+
65
+ ## Contributing
66
+ Contributions are welcome! See CONTRIBUTING.md for more details on how to get involved.
67
+
68
+ ## License
69
+ Distributed under the MIT License. See LICENSE for more information.
ufaas-0.1.0/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,45 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ufaas"
7
+ version = "0.1.0"
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
+ ]
36
+
37
+ [project.urls]
38
+ "Homepage" = "https://github.com/ufilesorg/ufiles-python"
39
+ "Bug Reports" = "https://github.com/ufilesorg/ufiles-python/issues"
40
+ "Funding" = "https://github.com/ufilesorg/ufiles-python"
41
+ "Say Thanks!" = "https://saythanks.io/to/mahdikiani"
42
+ "Source" = "https://github.com/ufilesorg/ufiles-python"
43
+
44
+ [tool.setuptools]
45
+ package-data = {"sample" = ["*.dat"]}
ufaas-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ from .ufaas import AsyncUFaaS, UFaaS
2
+
3
+ __all__ = ["UFaaS", "AsyncUFaaS"]
File without changes
@@ -0,0 +1,202 @@
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, metaclass=singleton.Singleton):
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
+ self.ufaas_base_url = ufaas_base_url
28
+ self.app_name = app_name
29
+ self.app_url = f"{ufaas_base_url}/{app_name}"
30
+ self.initiate_resources()
31
+
32
+ def initiate_resources(self, **kwargs):
33
+ pass
34
+
35
+
36
+ class AsyncApp(AsyncUssoSession, metaclass=singleton.Singleton):
37
+
38
+ def __init__(
39
+ self,
40
+ app_name: str = "saas",
41
+ *,
42
+ ufaas_base_url: str = os.getenv("UFAAS_URL"),
43
+ usso_base_url: str | None = os.getenv("USSO_URL"),
44
+ api_key: str | None = os.getenv("UFAAS_API_KEY"),
45
+ usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
46
+ refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
47
+ client: AsyncUssoSession | None = None,
48
+ ):
49
+ super().__init__(
50
+ usso_base_url=usso_base_url,
51
+ api_key=api_key,
52
+ usso_refresh_url=usso_refresh_url,
53
+ refresh_token=refresh_token,
54
+ client=client,
55
+ )
56
+ self.ufaas_base_url = ufaas_base_url
57
+ self.app_name = app_name
58
+ self.app_url = f"{ufaas_base_url}/{app_name}"
59
+ self.initiate_resources()
60
+
61
+ def initiate_resources(self, **kwargs):
62
+ pass
63
+
64
+
65
+ class Resource(UssoSession, metaclass=singleton.Singleton):
66
+ def __init__(
67
+ self,
68
+ app_name: str = "saas",
69
+ resource_name: str = "usages",
70
+ schema: type = dict,
71
+ *,
72
+ ufaas_base_url: str = os.getenv("UFAAS_URL"),
73
+ usso_base_url: str | None = os.getenv("USSO_URL"),
74
+ api_key: str | None = os.getenv("UFAAS_API_KEY"),
75
+ usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
76
+ refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
77
+ client: UssoSession | None = None,
78
+ ):
79
+ super().__init__(
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
+ self.ufaas_base_url = ufaas_base_url
87
+ self.app_name = app_name
88
+ self.resource_name = resource_name
89
+ self.app_url = f"{ufaas_base_url}/{app_name}"
90
+ self.resource_url = f"{self.app_url}/{resource_name}"
91
+ self._config_schemas(schema)
92
+
93
+ def _config_schemas(self, schema: type = dict, **kwargs):
94
+ self.schema = schema
95
+ if schema == dict:
96
+ self.list_response_schema = dict
97
+ else:
98
+ self.list_response_schema = PaginatedResponse[schema]
99
+ self.list_item_schema = schema
100
+ self.retrieve_response_schema = schema
101
+ self.create_response_schema = schema
102
+ self.update_response_schema = schema
103
+ self.delete_response_schema = schema
104
+
105
+ def list_items(self, offset: int = 0, limit: int = 10):
106
+ resp = self.get(self.resource_url, params={"offset": offset, "limit": limit})
107
+ resp.raise_for_status()
108
+ return self.list_response_schema(**resp.json())
109
+
110
+ def retrieve_item(self, uid: str):
111
+ resp = self.get(f"{self.resource_url}/{uid}")
112
+ resp.raise_for_status()
113
+ return self.retrieve_response_schema(**resp.json())
114
+
115
+ def create_item(self, obj: dict):
116
+ resp = self.post(self.resource_url, json=obj)
117
+ resp.raise_for_status()
118
+ return self.create_response_schema(**resp.json())
119
+
120
+ def update_item(self, uid: str, obj: dict):
121
+ resp = self.patch(f"{self.resource_url}/{uid}", json=obj)
122
+ resp.raise_for_status()
123
+ return self.update_response_schema(**resp.json())
124
+
125
+ def delete_item(self, uid: str):
126
+ resp = self.delete(f"{self.resource_url}/{uid}")
127
+ resp.raise_for_status()
128
+ return self.delete_response_schema(**resp.json())
129
+
130
+
131
+ class AsyncResource(AsyncUssoSession, metaclass=singleton.Singleton):
132
+
133
+ def __init__(
134
+ self,
135
+ app_name: str = "saas",
136
+ resource_name: str = "usages",
137
+ schema: type = dict,
138
+ *,
139
+ ufaas_base_url: str = os.getenv("UFAAS_URL"),
140
+ usso_base_url: str | None = os.getenv("USSO_URL"),
141
+ api_key: str | None = os.getenv("UFAAS_API_KEY"),
142
+ usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
143
+ refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
144
+ client: AsyncUssoSession | None = None,
145
+ ):
146
+ super().__init__(
147
+ app_name=app_name,
148
+ resource_name=resource_name,
149
+ schema=schema,
150
+ ufaas_base_url=ufaas_base_url,
151
+ usso_base_url=usso_base_url,
152
+ api_key=api_key,
153
+ usso_refresh_url=usso_refresh_url,
154
+ refresh_token=refresh_token,
155
+ client=client,
156
+ )
157
+
158
+ self.ufaas_base_url = ufaas_base_url
159
+ self.app_name = app_name
160
+ self.resource_name = resource_name
161
+ self.app_url = f"{ufaas_base_url}/{app_name}"
162
+ self.resource_url = f"{self.app_url}/{resource_name}"
163
+ self._config_schemas(schema)
164
+
165
+ def _config_schemas(self, schema: type = dict, **kwargs):
166
+ self.schema = schema
167
+ if schema == dict:
168
+ self.list_response_schema = dict
169
+ else:
170
+ self.list_response_schema = PaginatedResponse[schema]
171
+ self.list_item_schema = schema
172
+ self.retrieve_response_schema = schema
173
+ self.create_response_schema = schema
174
+ self.update_response_schema = schema
175
+ self.delete_response_schema = schema
176
+
177
+ async def list_items(self, offset: int = 0, limit: int = 10):
178
+ resp = await self.get(
179
+ self.resource_url, params={"offset": offset, "limit": limit}
180
+ )
181
+ resp.raise_for_status()
182
+ return self.list_response_schema(**await resp.json())
183
+
184
+ async def retrieve_item(self, uid: str):
185
+ resp = await self.get(f"{self.resource_url}/{uid}")
186
+ resp.raise_for_status()
187
+ return self.retrieve_response_schema(**await resp.json())
188
+
189
+ async def create_item(self, obj: dict):
190
+ resp = await self.post(self.resource_url, json=obj)
191
+ resp.raise_for_status()
192
+ return self.create_response_schema(**await resp.json())
193
+
194
+ async def update_item(self, uid: str, obj: dict):
195
+ resp = await self.patch(f"{self.resource_url}/{uid}", json=obj)
196
+ resp.raise_for_status()
197
+ return self.update_response_schema(**await resp.json())
198
+
199
+ async def delete_item(self, uid: str):
200
+ resp = await self.delete(f"{self.resource_url}/{uid}")
201
+ resp.raise_for_status()
202
+ return self.delete_response_schema(**await resp.json())
@@ -0,0 +1,3 @@
1
+ from saas import AsyncUsage, AsyncEnrollment, Enrollment, Usage, SaaS, AsyncSaaS
2
+
3
+ __all__ = ["AsyncUsage", "AsyncEnrollment", "Enrollment", "Usage", "SaaS", "AsyncSaaS"]
@@ -0,0 +1,157 @@
1
+ import os
2
+
3
+ from usso.session import AsyncUssoSession, UssoSession
4
+
5
+ from ..base_app import App, AsyncApp, Resource, AsyncResource
6
+ from .schemas import EnrollmentSchema, UsageSchema
7
+
8
+ class SaaS(App):
9
+ def __init__(
10
+ self,
11
+ *,
12
+ ufaas_base_url: str = os.getenv("UFAAS_URL"),
13
+ usso_base_url: str | None = os.getenv("USSO_URL"),
14
+ api_key: str | None = os.getenv("UFAAS_API_KEY"),
15
+ usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
16
+ refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
17
+ client: UssoSession | None = None,
18
+ ):
19
+ super().__init__(
20
+ app_name="saas",
21
+ ufaas_base_url=ufaas_base_url,
22
+ usso_base_url=usso_base_url,
23
+ api_key=api_key,
24
+ usso_refresh_url=usso_refresh_url,
25
+ refresh_token=refresh_token,
26
+ client=client,
27
+ )
28
+
29
+
30
+ def initiate_resources(self, **kwargs):
31
+ self.usages = Usage(client=self)
32
+ self.enrollments = Enrollment(client=self)
33
+
34
+
35
+ class AsyncSaaS(AsyncApp):
36
+
37
+ def __init__(
38
+ self,
39
+ *,
40
+ ufaas_base_url: str = os.getenv("UFAAS_URL"),
41
+ usso_base_url: str | None = os.getenv("USSO_URL"),
42
+ api_key: str | None = os.getenv("UFAAS_API_KEY"),
43
+ usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
44
+ refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
45
+ client: AsyncUssoSession | None = None,
46
+ ):
47
+ super().__init__(
48
+ app_name="saas",
49
+ ufaas_base_url=ufaas_base_url,
50
+ usso_base_url=usso_base_url,
51
+ api_key=api_key,
52
+ usso_refresh_url=usso_refresh_url,
53
+ refresh_token=refresh_token,
54
+ client=client,
55
+ )
56
+
57
+ def initiate_resources(self, **kwargs):
58
+ self.usages = AsyncUsage(client=self)
59
+ self.enrollments = AsyncEnrollment(client=self)
60
+
61
+
62
+ class Usage(Resource):
63
+
64
+ def __init__(
65
+ self,
66
+ *,
67
+ ufaas_base_url: str = os.getenv("UFAAS_URL"),
68
+ usso_base_url: str | None = os.getenv("USSO_URL"),
69
+ api_key: str | None = os.getenv("UFAAS_API_KEY"),
70
+ usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
71
+ refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
72
+ client: UssoSession | None = None,
73
+ ):
74
+ super().__init__(
75
+ app_name="saas",
76
+ resource_name="usages",
77
+ schema=UsageSchema,
78
+ ufaas_base_url=ufaas_base_url,
79
+ usso_base_url=usso_base_url,
80
+ api_key=api_key,
81
+ usso_refresh_url=usso_refresh_url,
82
+ refresh_token=refresh_token,
83
+ client=client,
84
+ )
85
+
86
+
87
+ class AsyncUsage(AsyncResource):
88
+
89
+ def __init__(
90
+ self,
91
+ *,
92
+ ufaas_base_url: str = os.getenv("UFAAS_URL"),
93
+ usso_base_url: str | None = os.getenv("USSO_URL"),
94
+ api_key: str | None = os.getenv("UFAAS_API_KEY"),
95
+ usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
96
+ refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
97
+ client: AsyncUssoSession | None = None,
98
+ ):
99
+ super().__init__(
100
+ app_name="saas",
101
+ resource_name="usages",
102
+ schema=UsageSchema,
103
+ ufaas_base_url=ufaas_base_url,
104
+ usso_base_url=usso_base_url,
105
+ api_key=api_key,
106
+ usso_refresh_url=usso_refresh_url,
107
+ refresh_token=refresh_token,
108
+ client=client,
109
+ )
110
+
111
+
112
+ class Enrollment(Resource):
113
+ def __init__(
114
+ self,
115
+ *,
116
+ ufaas_base_url: str = os.getenv("UFAAS_URL"),
117
+ usso_base_url: str | None = os.getenv("USSO_URL"),
118
+ api_key: str | None = os.getenv("UFAAS_API_KEY"),
119
+ usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
120
+ refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
121
+ client: UssoSession | None = None,
122
+ ):
123
+ super().__init__(
124
+ app_name="saas",
125
+ resource_name="enrollments",
126
+ schema=EnrollmentSchema,
127
+ ufaas_base_url=ufaas_base_url,
128
+ usso_base_url=usso_base_url,
129
+ api_key=api_key,
130
+ usso_refresh_url=usso_refresh_url,
131
+ refresh_token=refresh_token,
132
+ client=client,
133
+ )
134
+
135
+
136
+ class AsyncEnrollment(AsyncResource):
137
+ def __init__(
138
+ self,
139
+ *,
140
+ ufaas_base_url: str = os.getenv("UFAAS_URL"),
141
+ usso_base_url: str | None = os.getenv("USSO_URL"),
142
+ api_key: str | None = os.getenv("UFAAS_API_KEY"),
143
+ usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
144
+ refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
145
+ client: AsyncUssoSession | None = None,
146
+ ):
147
+ super().__init__(
148
+ app_name="saas",
149
+ resource_name="enrollments",
150
+ schema=EnrollmentSchema,
151
+ ufaas_base_url=ufaas_base_url,
152
+ usso_base_url=usso_base_url,
153
+ api_key=api_key,
154
+ usso_refresh_url=usso_refresh_url,
155
+ refresh_token=refresh_token,
156
+ client=client,
157
+ )
@@ -0,0 +1,67 @@
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 EnrollmentSchema(BusinessOwnedEntitySchema):
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
+ paid_at: datetime | None = None
51
+
52
+
53
+ class UsageConsumption(BaseModel):
54
+ enrollment_id: uuid.UUID
55
+ amount: Decimal
56
+ leftover_bundles: list[Bundle] = []
57
+
58
+
59
+ class UsageSchema(BusinessOwnedEntitySchema):
60
+ # enrollment_id: uuid.UUID
61
+ # asset: str
62
+ # amount: Decimal
63
+
64
+ consumptions: list[UsageConsumption]
65
+ asset: str
66
+ amount: Decimal
67
+ variant: str | None = None
@@ -0,0 +1,147 @@
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
+ pass
36
+
37
+
38
+ class WalletDetailSchema(BusinessOwnedEntitySchema):
39
+ balance: dict[str, Decimal] = {}
40
+ wallet_type: WalletType = WalletType.user
41
+ main_currency: Currency
42
+
43
+ model_config = ConfigDict(allow_inf_nan=True)
44
+
45
+ @field_serializer("balance")
46
+ def serialize_balance(self, balance: dict[str, Decimal]) -> dict[str, Decimal]:
47
+ return {k: (v if v.is_finite() else Decimal(0)) for k, v in balance.items()}
48
+
49
+ @field_serializer("wallet_type")
50
+ def serialize_wallet_type(self, wallet_type: WalletType) -> str:
51
+ return wallet_type.value
52
+
53
+ @field_serializer("main_currency")
54
+ def serialize_main_currency(self, main_currency: Currency) -> str:
55
+ return main_currency.value
56
+
57
+
58
+ class WalletCreateSchema(BaseModel):
59
+ user_id: uuid.UUID
60
+ meta_data: dict | None = None
61
+
62
+ wallet_type: WalletType = WalletType.user
63
+ main_currency: Currency = Currency.none
64
+
65
+ @model_validator(mode="before")
66
+ def validate_wallet_type(cls, values):
67
+ if values.get("wallet_type") and not values.get("main_currency"):
68
+ raise ValueError("main_currency is required for income wallet")
69
+ return values
70
+
71
+
72
+ class WalletUpdateSchema(BaseModel):
73
+ meta_data: dict | None = None
74
+
75
+
76
+ class WalletHoldSchema(BusinessOwnedEntitySchema):
77
+ wallet_id: uuid.UUID
78
+ currency: str
79
+ amount: Decimal
80
+ expires_at: datetime
81
+ status: str
82
+ description: str | None = None
83
+
84
+
85
+ class WalletHoldCreateSchema(BaseModel):
86
+ amount: Decimal
87
+ expires_at: datetime
88
+ status: str = "active"
89
+ meta_data: dict | None = None
90
+ description: str | None = None
91
+
92
+
93
+ class WalletHoldUpdateSchema(BaseModel):
94
+ expires_at: datetime | None = None
95
+ status: str | None = None
96
+ meta_data: dict | None = None
97
+ description: str | None = None
98
+
99
+
100
+ class TransactionSchema(BusinessOwnedEntitySchema):
101
+ proposal_id: uuid.UUID
102
+ wallet_id: uuid.UUID
103
+ amount: Decimal
104
+ currency: str
105
+ balance: Decimal
106
+ description: str | None = None
107
+ note: str | None = None
108
+
109
+ model_config = ConfigDict(allow_inf_nan=True)
110
+
111
+
112
+ class TransactionNoteUpdateSchema(BaseModel):
113
+ note: str
114
+
115
+
116
+ class Participant(BaseModel):
117
+ wallet_id: uuid.UUID
118
+ amount: Decimal
119
+
120
+
121
+ class ProposalSchema(BusinessOwnedEntitySchema):
122
+ issuer_id: uuid.UUID
123
+ amount: Decimal
124
+ description: str | None = None
125
+ note: str | None = None
126
+ currency: str
127
+ # status: str
128
+ task_status: str
129
+ participants: list[Participant]
130
+
131
+
132
+ class ProposalCreateSchema(BaseModel):
133
+ amount: Decimal
134
+ description: str | None = None
135
+ note: str | None = None
136
+ currency: str
137
+ task_status: Literal["draft", "init"] = "draft"
138
+ participants: list[Participant]
139
+ meta_data: dict | None = None
140
+
141
+
142
+ class ProposalUpdateSchema(BaseModel):
143
+ # status: str | None
144
+ task_status: Literal["init"] | None = None
145
+ description: str | None = None
146
+ note: str | None = None
147
+ 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,68 @@
1
+ import os
2
+
3
+ import singleton
4
+ from usso.session import AsyncUssoSession, UssoSession
5
+ from .apps.saas import SaaS, AsyncSaaS
6
+
7
+ class UFaaS(UssoSession, metaclass=singleton.Singleton):
8
+
9
+ def __init__(
10
+ self,
11
+ *,
12
+ ufaas_base_url: str = os.getenv("UFAAS_URL"),
13
+ usso_base_url: str | None = os.getenv("USSO_URL"),
14
+ api_key: str | None = os.getenv("UFAAS_API_KEY"),
15
+ usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
16
+ refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
17
+ client: "UssoSession" | None = None,
18
+ ):
19
+ UssoSession.__init__(
20
+ usso_base_url=usso_base_url,
21
+ api_key=api_key,
22
+ usso_refresh_url=usso_refresh_url,
23
+ refresh_token=refresh_token,
24
+ client=client,
25
+ )
26
+ if not ufaas_base_url and client and hasattr(client, "ufaas_base_url"):
27
+ ufaas_base_url = client.ufaas_base_url
28
+ assert ufaas_base_url, "UFAAS_URL is required"
29
+ if ufaas_base_url.endswith("/"):
30
+ ufaas_base_url = ufaas_base_url[:-1]
31
+ self.ufaas_base_url = ufaas_base_url
32
+
33
+ self.initiate_apps()
34
+
35
+ def initiate_apps(self):
36
+ self.saas = SaaS(client=self)
37
+
38
+
39
+ class AsyncUFaaS(AsyncUssoSession, metaclass=singleton.Singleton):
40
+
41
+ def __init__(
42
+ self,
43
+ *,
44
+ ufaas_base_url: str = os.getenv("UFILES_URL"),
45
+ usso_base_url: str | None = os.getenv("USSO_URL"),
46
+ api_key: str | None = os.getenv("UFILES_API_KEY"),
47
+ usso_refresh_url: str | None = os.getenv("USSO_REFRESH_URL"),
48
+ refresh_token: str | None = os.getenv("USSO_REFRESH_TOKEN"),
49
+ client: "AsyncUssoSession" | None = None,
50
+ ):
51
+ AsyncUssoSession.__init__(
52
+ usso_base_url=usso_base_url,
53
+ api_key=api_key,
54
+ usso_refresh_url=usso_refresh_url,
55
+ refresh_token=refresh_token,
56
+ client=client,
57
+ )
58
+ if not ufaas_base_url and client and hasattr(client, "ufaas_base_url"):
59
+ ufaas_base_url = client.ufaas_base_url
60
+ assert ufaas_base_url, "UFAAS_URL is required"
61
+ if ufaas_base_url.endswith("/"):
62
+ ufaas_base_url = ufaas_base_url[:-1]
63
+ self.ufaas_base_url = ufaas_base_url
64
+
65
+ self.initiate_apps()
66
+
67
+ def initiate_apps(self):
68
+ self.saas = AsyncSaaS(client=self)
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.1
2
+ Name: ufaas
3
+ Version: 0.1.0
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
+
50
+ # UFiles-Client
51
+
52
+ The Ufiles-Client is a SDK for ufiles servers.
53
+
54
+ ## Installation
55
+
56
+ Install the ufiles client using pip:
57
+
58
+ ```bash
59
+ pip install ufiles
60
+ ```
61
+
62
+ ## Quick Start
63
+ Follow the quick start guides in the documentation to integrate ufiles in your application.
64
+
65
+ ## Contributing
66
+ Contributions are welcome! See CONTRIBUTING.md for more details on how to get involved.
67
+
68
+ ## License
69
+ Distributed under the MIT License. See LICENSE for more information.
@@ -0,0 +1,17 @@
1
+ LICENSE.txt
2
+ README.md
3
+ pyproject.toml
4
+ src/ufaas/__init__.py
5
+ src/ufaas/schemas.py
6
+ src/ufaas/services.py
7
+ src/ufaas/ufaas.py
8
+ src/ufaas.egg-info/PKG-INFO
9
+ src/ufaas.egg-info/SOURCES.txt
10
+ src/ufaas.egg-info/dependency_links.txt
11
+ src/ufaas.egg-info/requires.txt
12
+ src/ufaas.egg-info/top_level.txt
13
+ src/ufaas/apps/__init__.py
14
+ src/ufaas/apps/base_app.py
15
+ src/ufaas/apps/saas/__init__.py
16
+ src/ufaas/apps/saas/saas.py
17
+ src/ufaas/apps/saas/schemas.py
@@ -0,0 +1,4 @@
1
+ pydantic>=2
2
+ pyjwt[crypto]
3
+ usso>=0.27.2
4
+ fastapi_mongo_base
@@ -0,0 +1 @@
1
+ ufaas