rest-canvas 0.8.1__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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 rklaus
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,382 @@
1
+ Metadata-Version: 2.4
2
+ Name: rest-canvas
3
+ Version: 0.8.1
4
+ Summary: OpenAPI-driven decorator library for easy predictable and safe REST API implementation in python
5
+ Author: rklaus
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://gitlab.com/overflask/rest-canvas
8
+ Project-URL: Tracker, https://gitlab.com/overflask/rest-canvas/-/issues
9
+ Project-URL: Changelog, https://gitlab.com/overflask/rest-canvas/-/blob/main/CHANGELOG.md
10
+ Keywords: openapi,rest,api,decorator,flask,django,validation,schema
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
17
+ Requires-Python: >=3.14
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: PyYAML>=6.0
21
+ Requires-Dist: jsonschema>=4.0
22
+ Requires-Dist: prance>=25.4
23
+ Requires-Dist: openapi-spec-validator>=0.7
24
+ Provides-Extra: flask
25
+ Requires-Dist: Flask>=2.0; extra == "flask"
26
+ Provides-Extra: django
27
+ Requires-Dist: Django>=3.2; extra == "django"
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=7.0; extra == "dev"
30
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
31
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
32
+ Requires-Dist: mypy>=1.0; extra == "dev"
33
+ Requires-Dist: Flask>=2.0; extra == "dev"
34
+ Requires-Dist: Django>=3.2; extra == "dev"
35
+ Dynamic: license-file
36
+
37
+ # REST Canvas
38
+
39
+ OpenAPI-driven decorator library for REST API endpoints in Django and Flask.
40
+
41
+ ## Overview
42
+
43
+ REST Canvas provides a decorator-based approach to building REST APIs that are automatically validated against OpenAPI specifications. It handles request validation, response formatting, pagination, sorting, filtering, and error handling based on your OpenAPI YAML files.
44
+
45
+ ## Features
46
+
47
+ - **OpenAPI-driven**: All validation and behavior driven by OpenAPI specifications
48
+ - **Framework-agnostic**: Works with Django, Flask, and standalone mode
49
+ - **Automatic validation**: Path parameters, query parameters, and request body validation at startup
50
+ - **Request-centric design**: All features accessible via unified Request object
51
+ - **Feature declaration**: Declare endpoint capabilities with simple markers (Fields, Sort, Filter)
52
+ - **Pagination support**: Both cursor-based and page-based pagination
53
+ - **RCQL filtering**: REST Canvas Query Language for powerful filtering (e.g., `status==active&age>18`)
54
+ - **Sort support**: Multi-field sorting with ascending/descending (e.g., `name,-createdAt`)
55
+ - **Detail level selection**: Control response verbosity with field groups (e.g., `minimal`, `full`)
56
+ - **Type safety**: Generic type hints for pagination: `Request[PagePagination]`
57
+ - **Testing utilities**: Built-in MockRequest for testing view functions in isolation
58
+
59
+ ## Installation
60
+
61
+ ```bash
62
+ # Install from Git (once published)
63
+ pip install git+https://github.com/yourusername/rest-canvas.git
64
+
65
+ # Or install in editable mode for development
66
+ pip install -e .
67
+
68
+ # With development dependencies
69
+ pip install -e ".[dev]"
70
+
71
+ # With Django
72
+ pip install -e ".[django]"
73
+
74
+ # With Flask
75
+ pip install -e ".[flask]"
76
+ ```
77
+
78
+ ## Configuration
79
+
80
+ REST Canvas is configured via the `Config` class, which allows you to customize behavior:
81
+
82
+ ```python
83
+ from rest_canvas import RestCanvas
84
+ from rest_canvas.core.config import Config
85
+ from rest_canvas.core.log import LoggerConfig, LogToConsole
86
+ from rest_canvas.core.log.logger_types import LogLevel
87
+
88
+ # Basic usage - starts with Standalone adapter
89
+ api = RestCanvas('openapi.yaml')
90
+
91
+ # Custom configuration
92
+ config = Config(
93
+ debug=True, # Enable debug mode
94
+ logger_config=LoggerConfig(
95
+ min_log_level=LogLevel.INFO, # Set logging level
96
+ enabled=True, # Enable/disable logging
97
+ processors=[LogToConsole()] # Custom log processors
98
+ )
99
+ )
100
+ api = RestCanvas('openapi.yaml', config=config)
101
+ ```
102
+
103
+ ### Configuration Options
104
+
105
+ - **debug**: Enable debug mode for additional logging and validation (default: `False`)
106
+ - **logger_config**: LoggerConfig instance for customizing logging behavior
107
+
108
+ ### Framework Adapter Selection
109
+
110
+ REST Canvas always starts with the **Standalone adapter**. The adapter automatically switches when you register routes:
111
+
112
+ ```python
113
+ # Flask - adapter switches when you call register_flask_routes()
114
+ api = RestCanvas('openapi.yaml')
115
+ api.register_flask_routes(app) # Now using FlaskAdapter
116
+
117
+ # Django - adapter switches when you call register_django_urls()
118
+ api = RestCanvas('openapi.yaml')
119
+ urlpatterns = api.register_django_urls() # Now using DjangoAdapter
120
+
121
+ # Standalone - no registration needed (default)
122
+ api = RestCanvas('openapi.yaml')
123
+ # Call endpoints directly - stays with StandaloneAdapter
124
+ ```
125
+
126
+ **Note:** You cannot switch adapters once routes are registered. Calling `register_flask_routes()` or `register_django_urls()` more than once will raise a `RuntimeError`.
127
+
128
+ ## Quick Start
129
+
130
+ ### Flask Example
131
+
132
+ ```python
133
+ from flask import Flask
134
+ from rest_canvas import RestCanvas, Request, PagePagination, Sort, Filter
135
+
136
+ app = Flask(__name__)
137
+
138
+ # Initialize REST API with OpenAPI spec
139
+ api = RestCanvas('openapi.yaml', mount_point='/api/v1')
140
+
141
+
142
+ # Declare features in decorator, access via request object
143
+ @api.endpoint('GET /users', Sort, Filter)
144
+ def list_users(request: Request[PagePagination]):
145
+ """List users with pagination, sorting, and filtering."""
146
+ users = User.query.all()
147
+
148
+ # Filter with RCQL: ?filter=status==active&age>18
149
+ if request.filter:
150
+ users = apply_filter(users, request.filter)
151
+
152
+ # Sort: ?sort=name,-createdAt (ascending name, descending createdAt)
153
+ if request.sort:
154
+ users = apply_sort(users, request.sort)
155
+
156
+ # Pagination automatically populated from query params
157
+ users_page = users[request.pagination.offset:request.pagination.limit]
158
+ request.pagination.total = len(users)
159
+
160
+ return [user.to_dict() for user in users_page]
161
+
162
+
163
+ # Auto-register all routes with Flask
164
+ api.register_flask_routes(app)
165
+ ```
166
+
167
+ ### Django Example
168
+
169
+ ```python
170
+ from rest_canvas import RestCanvas, Request, CursorPagination, DetailLevel
171
+
172
+ # Initialize REST API
173
+ api = RestCanvas('openapi.yaml', mount_point='/api/v1')
174
+
175
+
176
+ @api.endpoint('GET /users/{userId}', DetailLevel)
177
+ def get_user_by_id(request: Request, user_id: int):
178
+ """Get user by ID with detail level selection."""
179
+ user = User.objects.get(id=user_id)
180
+
181
+ # Return different fields based on request.detail_level
182
+ # Query: ?detailLevel=minimal
183
+ if request.detail_level == "minimal":
184
+ return {"id": user.id, "name": user.name}
185
+ else:
186
+ return user.to_dict()
187
+
188
+
189
+ # In urls.py
190
+ urlpatterns = api.register_django_urls()
191
+ ```
192
+
193
+ ### Key Features
194
+
195
+ - **Request-centric design**: All features (pagination, sort, filter, detail_level) accessible via `request` object
196
+ - **Feature declaration**: Declare features in decorator: `@api.endpoint('GET /path', Sort, Filter, DetailLevel)`
197
+ - **Type-safe**: Generic type hints for pagination: `Request[PagePagination]`
198
+ - **Automatic parsing**: Sort, filter, pagination, and detail level parameters parsed automatically
199
+ - **OpenAPI validation**: Function signatures and features validated against OpenAPI spec at startup
200
+ - **RCQL filtering**: Powerful query language with operators: `==`, `!=`, `<`, `<=`, `>`, `>=`, `=like=`
201
+ - **Multi-field sorting**: Sort by multiple fields with direction: `?sort=name,-createdAt`
202
+ - **Framework adapters**: Seamless adaptation for Flask, Django, and standalone modes
203
+
204
+ ## Development Status
205
+
206
+ **Current Version**: 0.1.0 (Alpha)
207
+
208
+ This library is in active development. Recent refactoring has established a modular, request-centric architecture.
209
+
210
+ ### Completed Features
211
+ - ✅ OpenAPI-driven endpoint validation
212
+ - ✅ Request-centric design with unified Request object
213
+ - ✅ Framework adapters (Django, Flask, Standalone)
214
+ - ✅ Feature declaration system (Sort, Filter, DetailLevel)
215
+ - ✅ Pagination (Page-based and Cursor-based)
216
+ - ✅ RCQL filtering with comparison and logical operators
217
+ - ✅ Multi-field sorting with ascending/descending
218
+ - ✅ Detail level (field group) selection
219
+ - ✅ Automatic type coercion for query parameters
220
+ - ✅ CamelCase to snake_case conversion
221
+ - ✅ Testing utilities (MockRequest)
222
+
223
+ ### In Progress
224
+ - 🔄 Additional ORM appliers for filtering and sorting
225
+ - 🔄 Enhanced error messages and validation
226
+ - 🔄 Documentation improvements
227
+
228
+ ## Requirements
229
+
230
+ - Python >= 3.8
231
+ - PyYAML >= 6.0
232
+ - jsonschema >= 4.0
233
+
234
+ ## Development
235
+
236
+ ```bash
237
+ # Install development dependencies
238
+ pip install -r requirements-dev.txt
239
+
240
+ # Run tests
241
+ pytest
242
+
243
+ # Run tests with coverage
244
+ pytest --cov=rest_api_decorator --cov-report=html
245
+
246
+ # Format code
247
+ black .
248
+
249
+ # Lint code
250
+ flake8 rest_api_decorator tests
251
+
252
+ # Type check
253
+ mypy rest_api_decorator
254
+ ```
255
+
256
+ ## Testing Your Views
257
+
258
+ The library provides `MockRequest` for testing your view functions in isolation:
259
+
260
+ ```python
261
+ from rest_canvas.testing import MockRequest
262
+
263
+ def test_list_users():
264
+ # Create mock request with pagination, sort, and filter
265
+ request = (MockRequest()
266
+ .with_page_pagination(page=1, size=10)
267
+ .with_sort('name,-createdAt')
268
+ .with_filter('status==active'))
269
+
270
+ # Call view function directly
271
+ result = list_users(request)
272
+
273
+ # Assert results
274
+ assert len(result) <= 10
275
+ assert request.pagination.total > 0
276
+
277
+ def test_get_user_by_id():
278
+ # Create mock request for single resource with detail level
279
+ request = MockRequest().with_detail_level('minimal')
280
+
281
+ # Call view with path parameter
282
+ result = get_user_by_id(request, user_id=42)
283
+
284
+ # Assert minimal fields returned
285
+ assert 'id' in result
286
+ assert 'name' in result
287
+ assert 'email' not in result # Not included in minimal
288
+ ```
289
+
290
+ ## Project Structure
291
+
292
+ ```
293
+ rest_canvas/
294
+ ├── public/ # Public API surface
295
+ │ ├── rest_canvas.py # RestCanvas class (main entry point)
296
+ │ ├── features.py # Feature markers (DetailLevel, Sort, Filter)
297
+ │ └── exceptions.py # API exceptions
298
+ ├── core/ # Core implementation
299
+ │ ├── endpoint/ # Endpoint decorator and validation
300
+ │ ├── request/ # Request wrapper and validation
301
+ │ ├── open_api/ # OpenAPI spec loading and parsing
302
+ │ ├── pagination/ # Page and cursor pagination
303
+ │ ├── query/ # Query parameter parsing (sort, filter)
304
+ │ ├── view/ # View function introspection
305
+ │ ├── envelope/ # Response envelope formatting
306
+ │ └── serializer.py # Datetime serialization
307
+ ├── adapters/ # Framework adapters
308
+ │ ├── django_adapter.py
309
+ │ ├── flask_adapter.py
310
+ │ └── standalone_adapter.py
311
+ ├── utils/ # Utilities
312
+ └── testing/ # Testing utilities (MockRequest)
313
+
314
+ tests/
315
+ ├── unit/ # Unit tests
316
+ ├── integration/ # Integration tests
317
+ └── fixtures/ # Test fixtures and OpenAPI specs
318
+ ```
319
+
320
+ ## Key Concepts
321
+
322
+ ### RCQL (REST Canvas Query Language)
323
+
324
+ RCQL provides a powerful filtering syntax using URL-friendly operators:
325
+
326
+ ```
327
+ # Comparison operators
328
+ ?filter=status==active # Equal
329
+ ?filter=age>18 # Greater than
330
+ ?filter=age<=65 # Less than or equal
331
+ ?filter=name=like=%john% # Pattern matching
332
+
333
+ # Logical operators (left-to-right evaluation)
334
+ ?filter=status==active&age>18 # AND
335
+ ?filter=role==admin|role==mod # OR
336
+
337
+ # Complex expressions
338
+ ?filter=status==active&age>18|premium==true
339
+ ```
340
+
341
+ ### Sort Syntax
342
+
343
+ Multi-field sorting with direction control:
344
+
345
+ ```
346
+ ?sort=name # Ascending by name
347
+ ?sort=-createdAt # Descending by createdAt
348
+ ?sort=name,-createdAt # Multiple fields
349
+ ```
350
+
351
+ ### Detail Levels
352
+
353
+ Control response verbosity with field groups defined in your OpenAPI spec:
354
+
355
+ ```
356
+ ?detailLevel=minimal # Only essential fields
357
+ ?detailLevel=full # All fields including relations
358
+ ```
359
+
360
+ ## Architecture
361
+
362
+ REST Canvas uses a **request-centric** design where all query features flow through a unified `Request` object:
363
+
364
+ 1. **Decorator declaration**: Features declared in `@api.endpoint()` decorator
365
+ 2. **Automatic parsing**: Query parameters parsed and validated at request time
366
+ 3. **Request population**: Parsed values populated in `request.sort`, `request.filter`, etc.
367
+ 4. **Type safety**: Generic type hints for pagination: `Request[PagePagination]`
368
+ 5. **Framework agnostic**: Same code works with Django, Flask, or standalone
369
+
370
+ This design ensures your view functions remain clean, testable, and framework-independent.
371
+
372
+ ## License
373
+
374
+ MIT License
375
+
376
+ ## Contributing
377
+
378
+ This is currently an internal project. Contribution guidelines will be added once the library reaches beta status.
379
+
380
+ ## Support
381
+
382
+ For issues and questions, please refer to the issue tracker (to be set up).