strawberry-alchemy 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.
- strawberry_alchemy-0.1.0/LICENSE +21 -0
- strawberry_alchemy-0.1.0/PKG-INFO +126 -0
- strawberry_alchemy-0.1.0/README.md +95 -0
- strawberry_alchemy-0.1.0/pyproject.toml +71 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/__init__.py +124 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/enums.py +9 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/exceptions.py +2 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/filtering/__init__.py +23 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/filtering/access_control.py +19 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/filtering/filter_builder.py +322 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/filtering/inputs.py +67 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/filtering/operators.py +23 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/mapping/__init__.py +19 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/mapping/sqlalchemy_to_gql.py +192 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/models/__init__.py +3 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/models/base.py +32 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/optimizer/__init__.py +38 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/optimizer/prefetch.py +246 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/optimizer/query_analyzer.py +198 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/optimizer/query_optimizer.py +920 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/permissions/__init__.py +41 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/permissions/base.py +114 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/permissions/checker.py +30 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/permissions/input_parser.py +57 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/permissions/resolver.py +13 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/permissions/resource_bag.py +31 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/permissions/types.py +24 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/py.typed +0 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/repository/__init__.py +11 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/repository/base.py +255 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/repository/deletion.py +52 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/schema/__init__.py +3 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/schema/base.py +39 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/types/__init__.py +17 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/types/base_node.py +89 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/types/connection.py +180 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/types/list_result.py +7 -0
- strawberry_alchemy-0.1.0/src/strawberry_alchemy/utils.py +21 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alteian
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: strawberry-alchemy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Toolkit for fast and easy strawberry+sqlalchemy apis
|
|
5
|
+
Keywords: graphql,strawberry,sqlalchemy,async,api,pagination,relay,filtering,permissions,crud,database
|
|
6
|
+
Author: David Roučka
|
|
7
|
+
Author-email: David Roučka <Alteian@proton.me>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Framework :: AsyncIO
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Dist: strawberry-graphql>=0.220
|
|
22
|
+
Requires-Dist: sqlalchemy
|
|
23
|
+
Requires-Dist: pydantic>=2.0
|
|
24
|
+
Requires-Dist: python-dateutil>=2.7
|
|
25
|
+
Requires-Python: >=3.13
|
|
26
|
+
Project-URL: Homepage, https://github.com/Alteian/strawberry-alchemy
|
|
27
|
+
Project-URL: Repository, https://github.com/Alteian/strawberry-alchemy
|
|
28
|
+
Project-URL: Issues, https://github.com/Alteian/strawberry-alchemy/issues
|
|
29
|
+
Project-URL: Changelog, https://github.com/Alteian/strawberry-alchemy/blob/master/CHANGELOG.md
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# strawberry-alchemy
|
|
33
|
+
|
|
34
|
+
<p align="center">
|
|
35
|
+
<em>Batteries-included toolkit for building <strong>Strawberry GraphQL</strong> APIs backed by <strong>SQLAlchemy</strong></em>
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<a href="https://github.com/Alteian/strawberry-alchemy/actions/workflows/ci.yml"><img src="https://github.com/Alteian/strawberry-alchemy/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
40
|
+
<a href="https://pypi.org/project/strawberry-alchemy"><img src="https://img.shields.io/pypi/v/strawberry-alchemy?color=%2334D058&label=pypi" alt="PyPI version"></a>
|
|
41
|
+
<a href="https://pypi.org/project/strawberry-alchemy"><img src="https://img.shields.io/pypi/pyversions/strawberry-alchemy.svg?color=%2334D058" alt="Python versions"></a>
|
|
42
|
+
<a href="https://github.com/Alteian/strawberry-alchemy/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-yellow.svg" alt="License: MIT"></a>
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
**Source Code**: [https://github.com/Alteian/strawberry-alchemy](https://github.com/Alteian/strawberry-alchemy)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Features
|
|
52
|
+
|
|
53
|
+
| Module | What it does |
|
|
54
|
+
|---|---|
|
|
55
|
+
| **QueryOptimizer** | Analyzes Strawberry selection sets and builds a single optimized SQLAlchemy query — automatic `joinedload` / `selectinload`, column deferral, annotation injection |
|
|
56
|
+
| **FilterBuilder** | Translates Strawberry input types into SQLAlchemy `WHERE` clauses using a declarative operator system |
|
|
57
|
+
| **Repository** | Generic async CRUD with delete, dependent-map cascading, and lifecycle hooks |
|
|
58
|
+
| **Types** | Relay `Connection` / `Edge` / `PageInfo` pagination, `ListResult`, `BaseNodeType` |
|
|
59
|
+
| **Mapping** | Async helpers to convert SQLAlchemy instances → Strawberry types respecting the selected field tree |
|
|
60
|
+
| **Permissions** | Protocol-based permission primitives: `IsAuthenticated`, `RolePermission`, `OwnerPermission`, `ObjectAccessPermission`, plus resolver pattern and resource-bag |
|
|
61
|
+
| **Models** | Tiny SQLAlchemy `DeclarativeBase` with UUID primary key, timestamps, and automatic table naming |
|
|
62
|
+
| **Utilities** | `camel_to_snake`, `DateTimeProcessor`, `Ordering` enum, common exceptions |
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install strawberry-alchemy
|
|
68
|
+
# or with uv
|
|
69
|
+
uv add strawberry-alchemy
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Quick Start
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
import strawberry
|
|
76
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
77
|
+
|
|
78
|
+
from strawberry_alchemy import (
|
|
79
|
+
BaseNodeType,
|
|
80
|
+
BaseRepository,
|
|
81
|
+
FilterBuilder,
|
|
82
|
+
OptimizedListConnection,
|
|
83
|
+
QueryOptimizer,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# 1. Define your SQLAlchemy model (or use the provided Base)
|
|
87
|
+
from strawberry_alchemy.models import Base
|
|
88
|
+
|
|
89
|
+
# 2. Define your Strawberry type
|
|
90
|
+
@strawberry.type
|
|
91
|
+
class BookType(BaseNodeType):
|
|
92
|
+
title: str
|
|
93
|
+
author: str
|
|
94
|
+
|
|
95
|
+
# 3. Use QueryOptimizer in your resolver
|
|
96
|
+
@strawberry.type
|
|
97
|
+
class Query:
|
|
98
|
+
@strawberry.field
|
|
99
|
+
async def books(self, info: strawberry.Info) -> list[BookType]:
|
|
100
|
+
optimizer = QueryOptimizer(session=info.context.db, info=info)
|
|
101
|
+
result = await optimizer.optimize_query(model=Book)
|
|
102
|
+
return result.items
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Development
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
git clone https://github.com/Alteian/strawberry-alchemy.git
|
|
109
|
+
cd strawberry-alchemy
|
|
110
|
+
uv sync
|
|
111
|
+
|
|
112
|
+
# Lint & test
|
|
113
|
+
uv run ruff check .
|
|
114
|
+
uv run pytest -v
|
|
115
|
+
|
|
116
|
+
# Build
|
|
117
|
+
uv build
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Contributing
|
|
121
|
+
|
|
122
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# strawberry-alchemy
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<em>Batteries-included toolkit for building <strong>Strawberry GraphQL</strong> APIs backed by <strong>SQLAlchemy</strong></em>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://github.com/Alteian/strawberry-alchemy/actions/workflows/ci.yml"><img src="https://github.com/Alteian/strawberry-alchemy/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
9
|
+
<a href="https://pypi.org/project/strawberry-alchemy"><img src="https://img.shields.io/pypi/v/strawberry-alchemy?color=%2334D058&label=pypi" alt="PyPI version"></a>
|
|
10
|
+
<a href="https://pypi.org/project/strawberry-alchemy"><img src="https://img.shields.io/pypi/pyversions/strawberry-alchemy.svg?color=%2334D058" alt="Python versions"></a>
|
|
11
|
+
<a href="https://github.com/Alteian/strawberry-alchemy/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-yellow.svg" alt="License: MIT"></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
**Source Code**: [https://github.com/Alteian/strawberry-alchemy](https://github.com/Alteian/strawberry-alchemy)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
| Module | What it does |
|
|
23
|
+
|---|---|
|
|
24
|
+
| **QueryOptimizer** | Analyzes Strawberry selection sets and builds a single optimized SQLAlchemy query — automatic `joinedload` / `selectinload`, column deferral, annotation injection |
|
|
25
|
+
| **FilterBuilder** | Translates Strawberry input types into SQLAlchemy `WHERE` clauses using a declarative operator system |
|
|
26
|
+
| **Repository** | Generic async CRUD with delete, dependent-map cascading, and lifecycle hooks |
|
|
27
|
+
| **Types** | Relay `Connection` / `Edge` / `PageInfo` pagination, `ListResult`, `BaseNodeType` |
|
|
28
|
+
| **Mapping** | Async helpers to convert SQLAlchemy instances → Strawberry types respecting the selected field tree |
|
|
29
|
+
| **Permissions** | Protocol-based permission primitives: `IsAuthenticated`, `RolePermission`, `OwnerPermission`, `ObjectAccessPermission`, plus resolver pattern and resource-bag |
|
|
30
|
+
| **Models** | Tiny SQLAlchemy `DeclarativeBase` with UUID primary key, timestamps, and automatic table naming |
|
|
31
|
+
| **Utilities** | `camel_to_snake`, `DateTimeProcessor`, `Ordering` enum, common exceptions |
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install strawberry-alchemy
|
|
37
|
+
# or with uv
|
|
38
|
+
uv add strawberry-alchemy
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import strawberry
|
|
45
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
46
|
+
|
|
47
|
+
from strawberry_alchemy import (
|
|
48
|
+
BaseNodeType,
|
|
49
|
+
BaseRepository,
|
|
50
|
+
FilterBuilder,
|
|
51
|
+
OptimizedListConnection,
|
|
52
|
+
QueryOptimizer,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# 1. Define your SQLAlchemy model (or use the provided Base)
|
|
56
|
+
from strawberry_alchemy.models import Base
|
|
57
|
+
|
|
58
|
+
# 2. Define your Strawberry type
|
|
59
|
+
@strawberry.type
|
|
60
|
+
class BookType(BaseNodeType):
|
|
61
|
+
title: str
|
|
62
|
+
author: str
|
|
63
|
+
|
|
64
|
+
# 3. Use QueryOptimizer in your resolver
|
|
65
|
+
@strawberry.type
|
|
66
|
+
class Query:
|
|
67
|
+
@strawberry.field
|
|
68
|
+
async def books(self, info: strawberry.Info) -> list[BookType]:
|
|
69
|
+
optimizer = QueryOptimizer(session=info.context.db, info=info)
|
|
70
|
+
result = await optimizer.optimize_query(model=Book)
|
|
71
|
+
return result.items
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Development
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
git clone https://github.com/Alteian/strawberry-alchemy.git
|
|
78
|
+
cd strawberry-alchemy
|
|
79
|
+
uv sync
|
|
80
|
+
|
|
81
|
+
# Lint & test
|
|
82
|
+
uv run ruff check .
|
|
83
|
+
uv run pytest -v
|
|
84
|
+
|
|
85
|
+
# Build
|
|
86
|
+
uv build
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Contributing
|
|
90
|
+
|
|
91
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "strawberry-alchemy"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Toolkit for fast and easy strawberry+sqlalchemy apis"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "David Roučka", email = "Alteian@proton.me" },
|
|
10
|
+
]
|
|
11
|
+
keywords = ["graphql", "strawberry", "sqlalchemy", "async", "api", "pagination", "relay", "filtering", "permissions", "crud", "database"]
|
|
12
|
+
requires-python = ">=3.13"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Framework :: AsyncIO",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.13",
|
|
21
|
+
"Programming Language :: Python :: 3.14",
|
|
22
|
+
"Topic :: Software Development :: Libraries",
|
|
23
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
|
+
"Typing :: Typed",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"strawberry-graphql>=0.220",
|
|
28
|
+
"sqlalchemy",
|
|
29
|
+
"pydantic>=2.0",
|
|
30
|
+
"python-dateutil>=2.7",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/Alteian/strawberry-alchemy"
|
|
35
|
+
Repository = "https://github.com/Alteian/strawberry-alchemy"
|
|
36
|
+
Issues = "https://github.com/Alteian/strawberry-alchemy/issues"
|
|
37
|
+
Changelog = "https://github.com/Alteian/strawberry-alchemy/blob/master/CHANGELOG.md"
|
|
38
|
+
|
|
39
|
+
[dependency-groups]
|
|
40
|
+
dev = [
|
|
41
|
+
"pytest>=9.1",
|
|
42
|
+
"pytest-asyncio>=1.4",
|
|
43
|
+
"ruff>=0.15",
|
|
44
|
+
"mypy>=2.1",
|
|
45
|
+
"aiosqlite>=0.22",
|
|
46
|
+
"pre-commit>=4.6",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[build-system]
|
|
50
|
+
requires = ["uv_build>=0.11.21"]
|
|
51
|
+
build-backend = "uv_build"
|
|
52
|
+
|
|
53
|
+
[tool.ruff]
|
|
54
|
+
target-version = "py313"
|
|
55
|
+
line-length = 120
|
|
56
|
+
|
|
57
|
+
[tool.ruff.lint]
|
|
58
|
+
select = ["E", "F", "W", "I", "N", "UP", "B", "A", "SIM", "TCH"]
|
|
59
|
+
ignore = ["E501"]
|
|
60
|
+
|
|
61
|
+
[tool.ruff.lint.per-file-ignores]
|
|
62
|
+
"src/strawberry_alchemy/repository/base.py" = ["A002"]
|
|
63
|
+
|
|
64
|
+
[tool.pytest.ini_options]
|
|
65
|
+
asyncio_mode = "auto"
|
|
66
|
+
testpaths = ["tests"]
|
|
67
|
+
|
|
68
|
+
[tool.mypy]
|
|
69
|
+
python_version = "3.13"
|
|
70
|
+
strict = true
|
|
71
|
+
plugins = ["strawberry.ext.mypy_plugin"]
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
from importlib.metadata import version as _version
|
|
2
|
+
|
|
3
|
+
__version__ = _version("strawberry-alchemy")
|
|
4
|
+
|
|
5
|
+
from strawberry_alchemy.enums import Ordering
|
|
6
|
+
from strawberry_alchemy.exceptions import NotFoundError
|
|
7
|
+
from strawberry_alchemy.filtering import (
|
|
8
|
+
AccessControlFilter,
|
|
9
|
+
BooleanFilter,
|
|
10
|
+
DateTimeFilter,
|
|
11
|
+
EnumFilter,
|
|
12
|
+
FilterBuilder,
|
|
13
|
+
FilterOperators,
|
|
14
|
+
IDFilter,
|
|
15
|
+
IntFilter,
|
|
16
|
+
StringFilter,
|
|
17
|
+
)
|
|
18
|
+
from strawberry_alchemy.mapping import (
|
|
19
|
+
map_sqlalchemy_list_to_types,
|
|
20
|
+
map_sqlalchemy_to_type,
|
|
21
|
+
)
|
|
22
|
+
from strawberry_alchemy.models import Base
|
|
23
|
+
from strawberry_alchemy.optimizer import (
|
|
24
|
+
AnnotateAncestry,
|
|
25
|
+
AnnotateAnyExists,
|
|
26
|
+
AnnotateCount,
|
|
27
|
+
AnnotateCustom,
|
|
28
|
+
AnnotateExists,
|
|
29
|
+
PrefetchRelated,
|
|
30
|
+
QueryAnalyzer,
|
|
31
|
+
QueryOptimizer,
|
|
32
|
+
QueryResult,
|
|
33
|
+
build_recursive_dependency_tree,
|
|
34
|
+
merge_dependency_trees,
|
|
35
|
+
normalize_dependency_fields,
|
|
36
|
+
optimize_field,
|
|
37
|
+
source_path_to_dependency_tree,
|
|
38
|
+
)
|
|
39
|
+
from strawberry_alchemy.permissions import (
|
|
40
|
+
BasePermissionResolver,
|
|
41
|
+
HasId,
|
|
42
|
+
IsAuthenticated,
|
|
43
|
+
ModelRegistryLike,
|
|
44
|
+
ObjectAccessPermission,
|
|
45
|
+
OwnerPermission,
|
|
46
|
+
PermissionContextLike,
|
|
47
|
+
ResourceInstances,
|
|
48
|
+
RolePermission,
|
|
49
|
+
UserLike,
|
|
50
|
+
extract_global_ids_from_info,
|
|
51
|
+
fetch_and_check_permissions,
|
|
52
|
+
map_ids_to_models,
|
|
53
|
+
)
|
|
54
|
+
from strawberry_alchemy.repository import (
|
|
55
|
+
BaseDeletionHandler,
|
|
56
|
+
BaseRepository,
|
|
57
|
+
)
|
|
58
|
+
from strawberry_alchemy.schema import BaseSchema
|
|
59
|
+
from strawberry_alchemy.types import (
|
|
60
|
+
BaseNodeType,
|
|
61
|
+
Edge,
|
|
62
|
+
ListResult,
|
|
63
|
+
OptimizedListConnection,
|
|
64
|
+
PageInfo,
|
|
65
|
+
SliceMetadata,
|
|
66
|
+
)
|
|
67
|
+
from strawberry_alchemy.utils import (
|
|
68
|
+
camel_to_snake,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
__all__ = (
|
|
72
|
+
"__version__",
|
|
73
|
+
"AccessControlFilter",
|
|
74
|
+
"AnnotateAncestry",
|
|
75
|
+
"AnnotateAnyExists",
|
|
76
|
+
"AnnotateCount",
|
|
77
|
+
"AnnotateCustom",
|
|
78
|
+
"AnnotateExists",
|
|
79
|
+
"Base",
|
|
80
|
+
"BaseDeletionHandler",
|
|
81
|
+
"BaseNodeType",
|
|
82
|
+
"BasePermissionResolver",
|
|
83
|
+
"BaseRepository",
|
|
84
|
+
"BaseSchema",
|
|
85
|
+
"BooleanFilter",
|
|
86
|
+
"DateTimeFilter",
|
|
87
|
+
"Edge",
|
|
88
|
+
"EnumFilter",
|
|
89
|
+
"FilterBuilder",
|
|
90
|
+
"FilterOperators",
|
|
91
|
+
"HasId",
|
|
92
|
+
"IDFilter",
|
|
93
|
+
"IntFilter",
|
|
94
|
+
"IsAuthenticated",
|
|
95
|
+
"ListResult",
|
|
96
|
+
"ModelRegistryLike",
|
|
97
|
+
"NotFoundError",
|
|
98
|
+
"ObjectAccessPermission",
|
|
99
|
+
"OptimizedListConnection",
|
|
100
|
+
"Ordering",
|
|
101
|
+
"OwnerPermission",
|
|
102
|
+
"PageInfo",
|
|
103
|
+
"PermissionContextLike",
|
|
104
|
+
"PrefetchRelated",
|
|
105
|
+
"QueryAnalyzer",
|
|
106
|
+
"QueryOptimizer",
|
|
107
|
+
"QueryResult",
|
|
108
|
+
"ResourceInstances",
|
|
109
|
+
"RolePermission",
|
|
110
|
+
"SliceMetadata",
|
|
111
|
+
"StringFilter",
|
|
112
|
+
"UserLike",
|
|
113
|
+
"camel_to_snake",
|
|
114
|
+
"extract_global_ids_from_info",
|
|
115
|
+
"fetch_and_check_permissions",
|
|
116
|
+
"map_ids_to_models",
|
|
117
|
+
"map_sqlalchemy_list_to_types",
|
|
118
|
+
"map_sqlalchemy_to_type",
|
|
119
|
+
"optimize_field",
|
|
120
|
+
"build_recursive_dependency_tree",
|
|
121
|
+
"merge_dependency_trees",
|
|
122
|
+
"normalize_dependency_fields",
|
|
123
|
+
"source_path_to_dependency_tree",
|
|
124
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from .access_control import AccessControlFilter
|
|
2
|
+
from .filter_builder import FilterBuilder
|
|
3
|
+
from .inputs import (
|
|
4
|
+
BooleanFilter,
|
|
5
|
+
DateTimeFilter,
|
|
6
|
+
EnumFilter,
|
|
7
|
+
IDFilter,
|
|
8
|
+
IntFilter,
|
|
9
|
+
StringFilter,
|
|
10
|
+
)
|
|
11
|
+
from .operators import FilterOperators
|
|
12
|
+
|
|
13
|
+
__all__ = (
|
|
14
|
+
"AccessControlFilter",
|
|
15
|
+
"BooleanFilter",
|
|
16
|
+
"DateTimeFilter",
|
|
17
|
+
"EnumFilter",
|
|
18
|
+
"FilterBuilder",
|
|
19
|
+
"FilterOperators",
|
|
20
|
+
"IDFilter",
|
|
21
|
+
"IntFilter",
|
|
22
|
+
"StringFilter",
|
|
23
|
+
)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AccessControlMeta(type):
|
|
5
|
+
def __init__(
|
|
6
|
+
cls: "type[AccessControlFilter]",
|
|
7
|
+
name: str,
|
|
8
|
+
bases: tuple[type, ...],
|
|
9
|
+
dct: dict[str, Any],
|
|
10
|
+
) -> None:
|
|
11
|
+
if name != "AccessControlFilter" and not name.endswith("AccessFilter"):
|
|
12
|
+
raise TypeError(f"Subclass {name} must end with 'AccessFilter'")
|
|
13
|
+
super().__init__(name, bases, dct)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AccessControlFilter(metaclass=AccessControlMeta):
|
|
17
|
+
@staticmethod
|
|
18
|
+
async def apply_filter(query: Any, model: type[Any], context_user: Any) -> Any:
|
|
19
|
+
raise NotImplementedError("Subclasses must implement the apply_filter method")
|