miso-client 0.4.0__tar.gz → 0.5.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.
Potentially problematic release.
This version of miso-client might be problematic. Click here for more details.
- {miso_client-0.4.0 → miso_client-0.5.0}/CHANGELOG.md +87 -0
- {miso_client-0.4.0/miso_client.egg-info → miso_client-0.5.0}/PKG-INFO +220 -1
- {miso_client-0.4.0 → miso_client-0.5.0}/README.md +219 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/__init__.py +39 -1
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/models/error_response.py +10 -1
- miso_client-0.5.0/miso_client/models/filter.py +140 -0
- miso_client-0.5.0/miso_client/models/pagination.py +66 -0
- miso_client-0.5.0/miso_client/models/sort.py +25 -0
- miso_client-0.5.0/miso_client/utils/error_utils.py +104 -0
- miso_client-0.5.0/miso_client/utils/filter.py +256 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/utils/http_client.py +107 -0
- miso_client-0.5.0/miso_client/utils/pagination.py +157 -0
- miso_client-0.5.0/miso_client/utils/sort.py +116 -0
- {miso_client-0.4.0 → miso_client-0.5.0/miso_client.egg-info}/PKG-INFO +220 -1
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client.egg-info/SOURCES.txt +8 -1
- {miso_client-0.4.0 → miso_client-0.5.0}/pyproject.toml +1 -1
- {miso_client-0.4.0 → miso_client-0.5.0}/setup.py +1 -1
- {miso_client-0.4.0 → miso_client-0.5.0}/LICENSE +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/MANIFEST.in +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/errors.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/models/__init__.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/models/config.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/py.typed +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/services/__init__.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/services/auth.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/services/cache.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/services/encryption.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/services/logger.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/services/permission.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/services/redis.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/services/role.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/utils/__init__.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/utils/config_loader.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/utils/data_masker.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/utils/internal_http_client.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/utils/jwt_tools.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client/utils/sensitive_fields_loader.py +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client.egg-info/dependency_links.txt +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client.egg-info/not-zip-safe +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client.egg-info/requires.txt +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/miso_client.egg-info/top_level.txt +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/pytest.ini +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/requirements-test.txt +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/requirements.txt +0 -0
- {miso_client-0.4.0 → miso_client-0.5.0}/setup.cfg +0 -0
|
@@ -5,6 +5,93 @@ All notable changes to the MisoClient SDK will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.0] - 2025-11-02
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Pagination Utilities**: Complete pagination support for list responses
|
|
13
|
+
- `Meta` and `PaginatedListResponse` Pydantic models for standardized paginated responses
|
|
14
|
+
- `parse_pagination_params()` function to parse `page` and `page_size` query parameters
|
|
15
|
+
- `create_meta_object()` function to construct pagination metadata objects
|
|
16
|
+
- `apply_pagination_to_array()` function for local pagination in tests/mocks
|
|
17
|
+
- `create_paginated_list_response()` function to wrap data with pagination metadata
|
|
18
|
+
- Support for both snake_case (`total_items`, `current_page`, `page_size`) and camelCase (`totalItems`, `currentPage`, `pageSize`) attribute access
|
|
19
|
+
- Full type safety with Pydantic models and generic type support
|
|
20
|
+
|
|
21
|
+
- **Filtering Utilities**: Comprehensive filtering support for API queries
|
|
22
|
+
- `FilterOption`, `FilterQuery`, and `FilterBuilder` classes for building filter queries
|
|
23
|
+
- `FilterOperator` type supporting: `eq`, `neq`, `in`, `nin`, `gt`, `lt`, `gte`, `lte`, `contains`, `like`
|
|
24
|
+
- `parse_filter_params()` function to parse `filter=field:op:value` query parameters
|
|
25
|
+
- `build_query_string()` function to convert `FilterQuery` objects to URL query strings
|
|
26
|
+
- `apply_filters()` function for local filtering in tests/mocks
|
|
27
|
+
- `FilterBuilder` class with fluent API for method chaining (e.g., `FilterBuilder().add('status', 'eq', 'active').add('region', 'in', ['eu', 'us'])`)
|
|
28
|
+
- URL encoding support for field names and values (comma separators preserved for array values)
|
|
29
|
+
- Integration with `/metadata/filter` endpoint through `FilterBuilder` compatibility with `AccessFieldFilter`
|
|
30
|
+
|
|
31
|
+
- **Sorting Utilities**: Sort parameter parsing and building
|
|
32
|
+
- `SortOption` Pydantic model with `field` and `order` (asc/desc) properties
|
|
33
|
+
- `parse_sort_params()` function to parse `sort=-field` query parameters
|
|
34
|
+
- `build_sort_string()` function to convert `SortOption` lists to query string format
|
|
35
|
+
- Support for multiple sort fields with ascending/descending order
|
|
36
|
+
- URL encoding for field names with special characters
|
|
37
|
+
|
|
38
|
+
- **Error Handling Utilities**: Enhanced error response transformation and handling
|
|
39
|
+
- `transform_error_to_snake_case()` function for converting error dictionaries to `ErrorResponse` objects
|
|
40
|
+
- `handle_api_error_snake_case()` function for creating `MisoClientError` from API error responses
|
|
41
|
+
- Support for both camelCase and snake_case field names in error responses
|
|
42
|
+
- Automatic parameter overriding (instance and status_code parameters override response data)
|
|
43
|
+
- Graceful handling of missing optional fields (title, instance, request_key)
|
|
44
|
+
|
|
45
|
+
- **HTTP Client Enhancements**: New helper methods for filtered and paginated requests
|
|
46
|
+
- `get_with_filters()` method for making GET requests with `FilterBuilder` support
|
|
47
|
+
- `get_paginated()` method for making GET requests with pagination parameters
|
|
48
|
+
- Automatic query string building from filter/sort/pagination options
|
|
49
|
+
- Flexible response parsing (returns `PaginatedListResponse` when format matches, raw response otherwise)
|
|
50
|
+
|
|
51
|
+
- **ErrorResponse Model Enhancements**:
|
|
52
|
+
- Added `request_key` field for error tracking (supports both `request_key` and `requestKey` aliases)
|
|
53
|
+
- Made `title` field optional (defaults to `None`) for graceful handling of missing titles
|
|
54
|
+
- Added `status_code` property getter for snake_case access (complements `statusCode` camelCase field)
|
|
55
|
+
- Full support for both snake_case and camelCase attribute access
|
|
56
|
+
|
|
57
|
+
- **Model Exports**: All new models and utilities exported from main module
|
|
58
|
+
- Pagination: `Meta`, `PaginatedListResponse`, `parse_pagination_params`, `create_meta_object`, `apply_pagination_to_array`, `create_paginated_list_response`
|
|
59
|
+
- Filtering: `FilterOperator`, `FilterOption`, `FilterQuery`, `FilterBuilder`, `parse_filter_params`, `build_query_string`, `apply_filters`
|
|
60
|
+
- Sorting: `SortOption`, `parse_sort_params`, `build_sort_string`
|
|
61
|
+
- Error: `transform_error_to_snake_case`, `handle_api_error_snake_case`
|
|
62
|
+
- All utilities follow snake_case naming convention matching Miso/Dataplane API conventions
|
|
63
|
+
|
|
64
|
+
### Changed
|
|
65
|
+
|
|
66
|
+
- **ErrorResponse Model**: Made `title` field optional to support APIs that don't provide titles
|
|
67
|
+
- Old: `title: str = Field(..., description="Human-readable error title")`
|
|
68
|
+
- New: `title: Optional[str] = Field(default=None, description="Human-readable error title")`
|
|
69
|
+
- Backward compatible - existing code with required titles still works
|
|
70
|
+
|
|
71
|
+
- **handle_api_error_snake_case Function**: Enhanced parameter override behavior
|
|
72
|
+
- `instance` parameter now overrides instance in response_data (was only set if missing)
|
|
73
|
+
- `status_code` parameter now always overrides status_code in response_data (was only set if missing)
|
|
74
|
+
- Better error message generation when title is missing
|
|
75
|
+
|
|
76
|
+
### Technical Improvements
|
|
77
|
+
|
|
78
|
+
- **Type Safety**: Full type hints throughout all new utilities and models
|
|
79
|
+
- **Pydantic Models**: All new data structures use Pydantic for validation and serialization
|
|
80
|
+
- **Property Getters**: Added property getters to support both snake_case and camelCase attribute access in models
|
|
81
|
+
- **URL Encoding**: Smart encoding that preserves comma delimiters in array filter values
|
|
82
|
+
- **Comprehensive Tests**: 123 unit tests covering all utilities with 100% coverage for models and utilities
|
|
83
|
+
- **Documentation**: Complete README documentation with usage examples for all utilities
|
|
84
|
+
- **Snake_case Convention**: All utilities follow Python snake_case naming to match Miso/Dataplane API conventions
|
|
85
|
+
|
|
86
|
+
### Documentation
|
|
87
|
+
|
|
88
|
+
- Added comprehensive README section for pagination, filtering, and sorting utilities
|
|
89
|
+
- Usage examples for all utilities including combined usage patterns
|
|
90
|
+
- Integration examples with `/metadata/filter` endpoint
|
|
91
|
+
- Type hints and docstrings for all public APIs
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
8
95
|
## [0.4.0] - 2025-11-02
|
|
9
96
|
|
|
10
97
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: miso-client
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Python client SDK for AI Fabrix authentication, authorization, and logging
|
|
5
5
|
Home-page: https://github.com/aifabrix/miso-client-python
|
|
6
6
|
Author: AI Fabrix Team
|
|
@@ -611,6 +611,225 @@ print(error_response.statusCode) # 422
|
|
|
611
611
|
print(error_response.instance) # "/api/endpoint"
|
|
612
612
|
```
|
|
613
613
|
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
### Pagination, Filtering, and Sorting Utilities
|
|
617
|
+
|
|
618
|
+
**What happens:** The SDK provides reusable utilities for pagination, filtering, sorting, and error handling that work with any API endpoint.
|
|
619
|
+
|
|
620
|
+
#### Pagination
|
|
621
|
+
|
|
622
|
+
**Pagination Parameters:**
|
|
623
|
+
- `page`: Page number (1-based, defaults to 1)
|
|
624
|
+
- `page_size`: Number of items per page (defaults to 25)
|
|
625
|
+
|
|
626
|
+
```python
|
|
627
|
+
from miso_client import (
|
|
628
|
+
parse_pagination_params,
|
|
629
|
+
create_paginated_list_response,
|
|
630
|
+
PaginatedListResponse,
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
# Parse pagination from query parameters
|
|
634
|
+
params = {"page": "1", "page_size": "25"}
|
|
635
|
+
current_page, page_size = parse_pagination_params(params)
|
|
636
|
+
|
|
637
|
+
# Create paginated response
|
|
638
|
+
items = [{"id": 1}, {"id": 2}]
|
|
639
|
+
response = create_paginated_list_response(
|
|
640
|
+
items,
|
|
641
|
+
total_items=120,
|
|
642
|
+
current_page=1,
|
|
643
|
+
page_size=25,
|
|
644
|
+
type="item"
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
# Response structure:
|
|
648
|
+
# {
|
|
649
|
+
# "meta": {
|
|
650
|
+
# "total_items": 120,
|
|
651
|
+
# "current_page": 1,
|
|
652
|
+
# "page_size": 25,
|
|
653
|
+
# "type": "item"
|
|
654
|
+
# },
|
|
655
|
+
# "data": [{"id": 1}, {"id": 2}]
|
|
656
|
+
# }
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
#### Filtering
|
|
660
|
+
|
|
661
|
+
**Filter Operators:** `eq`, `neq`, `in`, `nin`, `gt`, `lt`, `gte`, `lte`, `contains`, `like`
|
|
662
|
+
|
|
663
|
+
**Filter Format:** `field:op:value` (e.g., `status:eq:active`)
|
|
664
|
+
|
|
665
|
+
```python
|
|
666
|
+
from miso_client import FilterBuilder, parse_filter_params, build_query_string
|
|
667
|
+
|
|
668
|
+
# Dynamic filter building with FilterBuilder
|
|
669
|
+
filter_builder = FilterBuilder() \
|
|
670
|
+
.add('status', 'eq', 'active') \
|
|
671
|
+
.add('region', 'in', ['eu', 'us']) \
|
|
672
|
+
.add('created_at', 'gte', '2024-01-01')
|
|
673
|
+
|
|
674
|
+
# Get query string
|
|
675
|
+
query_string = filter_builder.to_query_string()
|
|
676
|
+
# Returns: "filter=status:eq:active&filter=region:in:eu,us&filter=created_at:gte:2024-01-01"
|
|
677
|
+
|
|
678
|
+
# Parse existing filter parameters
|
|
679
|
+
params = {'filter': ['status:eq:active', 'region:in:eu,us']}
|
|
680
|
+
filters = parse_filter_params(params)
|
|
681
|
+
# Returns: [FilterOption(field='status', op='eq', value='active'), ...]
|
|
682
|
+
|
|
683
|
+
# Use with HTTP client
|
|
684
|
+
response = await client.http_client.get_with_filters(
|
|
685
|
+
'/api/items',
|
|
686
|
+
filter_builder=filter_builder
|
|
687
|
+
)
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
**Building Complete Filter Queries:**
|
|
691
|
+
|
|
692
|
+
```python
|
|
693
|
+
from miso_client import FilterQuery, FilterOption, build_query_string
|
|
694
|
+
|
|
695
|
+
# Create filter query with filters, sort, pagination, and fields
|
|
696
|
+
filter_query = FilterQuery(
|
|
697
|
+
filters=[
|
|
698
|
+
FilterOption(field='status', op='eq', value='active'),
|
|
699
|
+
FilterOption(field='region', op='in', value=['eu', 'us'])
|
|
700
|
+
],
|
|
701
|
+
sort=['-updated_at', 'created_at'],
|
|
702
|
+
page=1,
|
|
703
|
+
page_size=25,
|
|
704
|
+
fields=['id', 'name', 'status']
|
|
705
|
+
)
|
|
706
|
+
|
|
707
|
+
# Build query string
|
|
708
|
+
query_string = build_query_string(filter_query)
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
#### Sorting
|
|
712
|
+
|
|
713
|
+
**Sort Format:** `-field` for descending, `field` for ascending (e.g., `-updated_at`, `created_at`)
|
|
714
|
+
|
|
715
|
+
```python
|
|
716
|
+
from miso_client import parse_sort_params, build_sort_string, SortOption
|
|
717
|
+
|
|
718
|
+
# Parse sort parameters
|
|
719
|
+
params = {'sort': '-updated_at'}
|
|
720
|
+
sort_options = parse_sort_params(params)
|
|
721
|
+
# Returns: [SortOption(field='updated_at', order='desc')]
|
|
722
|
+
|
|
723
|
+
# Parse multiple sorts
|
|
724
|
+
params = {'sort': ['-updated_at', 'created_at']}
|
|
725
|
+
sort_options = parse_sort_params(params)
|
|
726
|
+
# Returns: [
|
|
727
|
+
# SortOption(field='updated_at', order='desc'),
|
|
728
|
+
# SortOption(field='created_at', order='asc')
|
|
729
|
+
# ]
|
|
730
|
+
|
|
731
|
+
# Build sort string
|
|
732
|
+
sort_options = [
|
|
733
|
+
SortOption(field='updated_at', order='desc'),
|
|
734
|
+
SortOption(field='created_at', order='asc')
|
|
735
|
+
]
|
|
736
|
+
sort_string = build_sort_string(sort_options)
|
|
737
|
+
# Returns: "-updated_at,created_at"
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
#### Combined Usage
|
|
741
|
+
|
|
742
|
+
**Pagination + Filter + Sort:**
|
|
743
|
+
|
|
744
|
+
```python
|
|
745
|
+
from miso_client import (
|
|
746
|
+
FilterBuilder,
|
|
747
|
+
FilterQuery,
|
|
748
|
+
build_query_string,
|
|
749
|
+
parse_pagination_params,
|
|
750
|
+
)
|
|
751
|
+
|
|
752
|
+
# Build filters
|
|
753
|
+
filter_builder = FilterBuilder() \
|
|
754
|
+
.add('status', 'eq', 'active') \
|
|
755
|
+
.add('region', 'in', ['eu', 'us'])
|
|
756
|
+
|
|
757
|
+
# Parse pagination
|
|
758
|
+
params = {'page': '1', 'page_size': '25'}
|
|
759
|
+
current_page, page_size = parse_pagination_params(params)
|
|
760
|
+
|
|
761
|
+
# Create complete query
|
|
762
|
+
filter_query = FilterQuery(
|
|
763
|
+
filters=filter_builder.build(),
|
|
764
|
+
sort=['-updated_at'],
|
|
765
|
+
page=current_page,
|
|
766
|
+
page_size=page_size
|
|
767
|
+
)
|
|
768
|
+
|
|
769
|
+
# Build query string
|
|
770
|
+
query_string = build_query_string(filter_query)
|
|
771
|
+
|
|
772
|
+
# Use with HTTP client
|
|
773
|
+
response = await client.http_client.get_with_filters(
|
|
774
|
+
'/api/items',
|
|
775
|
+
filter_builder=filter_builder,
|
|
776
|
+
params={'page': current_page, 'page_size': page_size}
|
|
777
|
+
)
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
**Or use pagination helper:**
|
|
781
|
+
|
|
782
|
+
```python
|
|
783
|
+
# Get paginated response
|
|
784
|
+
response = await client.http_client.get_paginated(
|
|
785
|
+
'/api/items',
|
|
786
|
+
page=1,
|
|
787
|
+
page_size=25
|
|
788
|
+
)
|
|
789
|
+
|
|
790
|
+
# Response is automatically parsed as PaginatedListResponse
|
|
791
|
+
print(response.meta.total_items) # 120
|
|
792
|
+
print(response.meta.current_page) # 1
|
|
793
|
+
print(len(response.data)) # 25
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
#### Metadata Filter Integration
|
|
797
|
+
|
|
798
|
+
**Working with `/metadata/filter` endpoint:**
|
|
799
|
+
|
|
800
|
+
```python
|
|
801
|
+
# Get metadata filters from endpoint
|
|
802
|
+
metadata_response = await client.http_client.post(
|
|
803
|
+
"/api/v1/metadata/filter",
|
|
804
|
+
{"documentStorageKey": "my-doc-storage"}
|
|
805
|
+
)
|
|
806
|
+
|
|
807
|
+
# Convert AccessFieldFilter to FilterBuilder
|
|
808
|
+
filter_builder = FilterBuilder()
|
|
809
|
+
for access_filter in metadata_response.mandatoryFilters:
|
|
810
|
+
filter_builder.add(access_filter.field, 'in', access_filter.values)
|
|
811
|
+
|
|
812
|
+
# Use with query utilities
|
|
813
|
+
query_string = filter_builder.to_query_string()
|
|
814
|
+
|
|
815
|
+
# Apply to API requests
|
|
816
|
+
response = await client.http_client.get_with_filters(
|
|
817
|
+
'/api/items',
|
|
818
|
+
filter_builder=filter_builder
|
|
819
|
+
)
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
**Features:**
|
|
823
|
+
|
|
824
|
+
- **Snake_case Convention**: All utilities use snake_case to match Miso/Dataplane API
|
|
825
|
+
- **Type Safety**: Full type hints with Pydantic models
|
|
826
|
+
- **Dynamic Filtering**: FilterBuilder supports method chaining for complex filters
|
|
827
|
+
- **Local Testing**: `apply_filters()` and `apply_pagination_to_array()` for local filtering/pagination in tests
|
|
828
|
+
- **URL Encoding**: Automatic URL encoding for field names and values
|
|
829
|
+
- **Backward Compatible**: Works alongside existing HTTP client methods
|
|
830
|
+
|
|
831
|
+
---
|
|
832
|
+
|
|
614
833
|
### Common Tasks
|
|
615
834
|
|
|
616
835
|
**Add authentication middleware (FastAPI):**
|
|
@@ -562,6 +562,225 @@ print(error_response.statusCode) # 422
|
|
|
562
562
|
print(error_response.instance) # "/api/endpoint"
|
|
563
563
|
```
|
|
564
564
|
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
### Pagination, Filtering, and Sorting Utilities
|
|
568
|
+
|
|
569
|
+
**What happens:** The SDK provides reusable utilities for pagination, filtering, sorting, and error handling that work with any API endpoint.
|
|
570
|
+
|
|
571
|
+
#### Pagination
|
|
572
|
+
|
|
573
|
+
**Pagination Parameters:**
|
|
574
|
+
- `page`: Page number (1-based, defaults to 1)
|
|
575
|
+
- `page_size`: Number of items per page (defaults to 25)
|
|
576
|
+
|
|
577
|
+
```python
|
|
578
|
+
from miso_client import (
|
|
579
|
+
parse_pagination_params,
|
|
580
|
+
create_paginated_list_response,
|
|
581
|
+
PaginatedListResponse,
|
|
582
|
+
)
|
|
583
|
+
|
|
584
|
+
# Parse pagination from query parameters
|
|
585
|
+
params = {"page": "1", "page_size": "25"}
|
|
586
|
+
current_page, page_size = parse_pagination_params(params)
|
|
587
|
+
|
|
588
|
+
# Create paginated response
|
|
589
|
+
items = [{"id": 1}, {"id": 2}]
|
|
590
|
+
response = create_paginated_list_response(
|
|
591
|
+
items,
|
|
592
|
+
total_items=120,
|
|
593
|
+
current_page=1,
|
|
594
|
+
page_size=25,
|
|
595
|
+
type="item"
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
# Response structure:
|
|
599
|
+
# {
|
|
600
|
+
# "meta": {
|
|
601
|
+
# "total_items": 120,
|
|
602
|
+
# "current_page": 1,
|
|
603
|
+
# "page_size": 25,
|
|
604
|
+
# "type": "item"
|
|
605
|
+
# },
|
|
606
|
+
# "data": [{"id": 1}, {"id": 2}]
|
|
607
|
+
# }
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
#### Filtering
|
|
611
|
+
|
|
612
|
+
**Filter Operators:** `eq`, `neq`, `in`, `nin`, `gt`, `lt`, `gte`, `lte`, `contains`, `like`
|
|
613
|
+
|
|
614
|
+
**Filter Format:** `field:op:value` (e.g., `status:eq:active`)
|
|
615
|
+
|
|
616
|
+
```python
|
|
617
|
+
from miso_client import FilterBuilder, parse_filter_params, build_query_string
|
|
618
|
+
|
|
619
|
+
# Dynamic filter building with FilterBuilder
|
|
620
|
+
filter_builder = FilterBuilder() \
|
|
621
|
+
.add('status', 'eq', 'active') \
|
|
622
|
+
.add('region', 'in', ['eu', 'us']) \
|
|
623
|
+
.add('created_at', 'gte', '2024-01-01')
|
|
624
|
+
|
|
625
|
+
# Get query string
|
|
626
|
+
query_string = filter_builder.to_query_string()
|
|
627
|
+
# Returns: "filter=status:eq:active&filter=region:in:eu,us&filter=created_at:gte:2024-01-01"
|
|
628
|
+
|
|
629
|
+
# Parse existing filter parameters
|
|
630
|
+
params = {'filter': ['status:eq:active', 'region:in:eu,us']}
|
|
631
|
+
filters = parse_filter_params(params)
|
|
632
|
+
# Returns: [FilterOption(field='status', op='eq', value='active'), ...]
|
|
633
|
+
|
|
634
|
+
# Use with HTTP client
|
|
635
|
+
response = await client.http_client.get_with_filters(
|
|
636
|
+
'/api/items',
|
|
637
|
+
filter_builder=filter_builder
|
|
638
|
+
)
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
**Building Complete Filter Queries:**
|
|
642
|
+
|
|
643
|
+
```python
|
|
644
|
+
from miso_client import FilterQuery, FilterOption, build_query_string
|
|
645
|
+
|
|
646
|
+
# Create filter query with filters, sort, pagination, and fields
|
|
647
|
+
filter_query = FilterQuery(
|
|
648
|
+
filters=[
|
|
649
|
+
FilterOption(field='status', op='eq', value='active'),
|
|
650
|
+
FilterOption(field='region', op='in', value=['eu', 'us'])
|
|
651
|
+
],
|
|
652
|
+
sort=['-updated_at', 'created_at'],
|
|
653
|
+
page=1,
|
|
654
|
+
page_size=25,
|
|
655
|
+
fields=['id', 'name', 'status']
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
# Build query string
|
|
659
|
+
query_string = build_query_string(filter_query)
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
#### Sorting
|
|
663
|
+
|
|
664
|
+
**Sort Format:** `-field` for descending, `field` for ascending (e.g., `-updated_at`, `created_at`)
|
|
665
|
+
|
|
666
|
+
```python
|
|
667
|
+
from miso_client import parse_sort_params, build_sort_string, SortOption
|
|
668
|
+
|
|
669
|
+
# Parse sort parameters
|
|
670
|
+
params = {'sort': '-updated_at'}
|
|
671
|
+
sort_options = parse_sort_params(params)
|
|
672
|
+
# Returns: [SortOption(field='updated_at', order='desc')]
|
|
673
|
+
|
|
674
|
+
# Parse multiple sorts
|
|
675
|
+
params = {'sort': ['-updated_at', 'created_at']}
|
|
676
|
+
sort_options = parse_sort_params(params)
|
|
677
|
+
# Returns: [
|
|
678
|
+
# SortOption(field='updated_at', order='desc'),
|
|
679
|
+
# SortOption(field='created_at', order='asc')
|
|
680
|
+
# ]
|
|
681
|
+
|
|
682
|
+
# Build sort string
|
|
683
|
+
sort_options = [
|
|
684
|
+
SortOption(field='updated_at', order='desc'),
|
|
685
|
+
SortOption(field='created_at', order='asc')
|
|
686
|
+
]
|
|
687
|
+
sort_string = build_sort_string(sort_options)
|
|
688
|
+
# Returns: "-updated_at,created_at"
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
#### Combined Usage
|
|
692
|
+
|
|
693
|
+
**Pagination + Filter + Sort:**
|
|
694
|
+
|
|
695
|
+
```python
|
|
696
|
+
from miso_client import (
|
|
697
|
+
FilterBuilder,
|
|
698
|
+
FilterQuery,
|
|
699
|
+
build_query_string,
|
|
700
|
+
parse_pagination_params,
|
|
701
|
+
)
|
|
702
|
+
|
|
703
|
+
# Build filters
|
|
704
|
+
filter_builder = FilterBuilder() \
|
|
705
|
+
.add('status', 'eq', 'active') \
|
|
706
|
+
.add('region', 'in', ['eu', 'us'])
|
|
707
|
+
|
|
708
|
+
# Parse pagination
|
|
709
|
+
params = {'page': '1', 'page_size': '25'}
|
|
710
|
+
current_page, page_size = parse_pagination_params(params)
|
|
711
|
+
|
|
712
|
+
# Create complete query
|
|
713
|
+
filter_query = FilterQuery(
|
|
714
|
+
filters=filter_builder.build(),
|
|
715
|
+
sort=['-updated_at'],
|
|
716
|
+
page=current_page,
|
|
717
|
+
page_size=page_size
|
|
718
|
+
)
|
|
719
|
+
|
|
720
|
+
# Build query string
|
|
721
|
+
query_string = build_query_string(filter_query)
|
|
722
|
+
|
|
723
|
+
# Use with HTTP client
|
|
724
|
+
response = await client.http_client.get_with_filters(
|
|
725
|
+
'/api/items',
|
|
726
|
+
filter_builder=filter_builder,
|
|
727
|
+
params={'page': current_page, 'page_size': page_size}
|
|
728
|
+
)
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
**Or use pagination helper:**
|
|
732
|
+
|
|
733
|
+
```python
|
|
734
|
+
# Get paginated response
|
|
735
|
+
response = await client.http_client.get_paginated(
|
|
736
|
+
'/api/items',
|
|
737
|
+
page=1,
|
|
738
|
+
page_size=25
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
# Response is automatically parsed as PaginatedListResponse
|
|
742
|
+
print(response.meta.total_items) # 120
|
|
743
|
+
print(response.meta.current_page) # 1
|
|
744
|
+
print(len(response.data)) # 25
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
#### Metadata Filter Integration
|
|
748
|
+
|
|
749
|
+
**Working with `/metadata/filter` endpoint:**
|
|
750
|
+
|
|
751
|
+
```python
|
|
752
|
+
# Get metadata filters from endpoint
|
|
753
|
+
metadata_response = await client.http_client.post(
|
|
754
|
+
"/api/v1/metadata/filter",
|
|
755
|
+
{"documentStorageKey": "my-doc-storage"}
|
|
756
|
+
)
|
|
757
|
+
|
|
758
|
+
# Convert AccessFieldFilter to FilterBuilder
|
|
759
|
+
filter_builder = FilterBuilder()
|
|
760
|
+
for access_filter in metadata_response.mandatoryFilters:
|
|
761
|
+
filter_builder.add(access_filter.field, 'in', access_filter.values)
|
|
762
|
+
|
|
763
|
+
# Use with query utilities
|
|
764
|
+
query_string = filter_builder.to_query_string()
|
|
765
|
+
|
|
766
|
+
# Apply to API requests
|
|
767
|
+
response = await client.http_client.get_with_filters(
|
|
768
|
+
'/api/items',
|
|
769
|
+
filter_builder=filter_builder
|
|
770
|
+
)
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
**Features:**
|
|
774
|
+
|
|
775
|
+
- **Snake_case Convention**: All utilities use snake_case to match Miso/Dataplane API
|
|
776
|
+
- **Type Safety**: Full type hints with Pydantic models
|
|
777
|
+
- **Dynamic Filtering**: FilterBuilder supports method chaining for complex filters
|
|
778
|
+
- **Local Testing**: `apply_filters()` and `apply_pagination_to_array()` for local filtering/pagination in tests
|
|
779
|
+
- **URL Encoding**: Automatic URL encoding for field names and values
|
|
780
|
+
- **Backward Compatible**: Works alongside existing HTTP client methods
|
|
781
|
+
|
|
782
|
+
---
|
|
783
|
+
|
|
565
784
|
### Common Tasks
|
|
566
785
|
|
|
567
786
|
**Add authentication middleware (FastAPI):**
|
|
@@ -27,6 +27,9 @@ from .models.config import (
|
|
|
27
27
|
UserInfo,
|
|
28
28
|
)
|
|
29
29
|
from .models.error_response import ErrorResponse
|
|
30
|
+
from .models.filter import FilterBuilder, FilterOperator, FilterOption, FilterQuery
|
|
31
|
+
from .models.pagination import Meta, PaginatedListResponse
|
|
32
|
+
from .models.sort import SortOption
|
|
30
33
|
from .services.auth import AuthService
|
|
31
34
|
from .services.cache import CacheService
|
|
32
35
|
from .services.encryption import EncryptionService
|
|
@@ -35,10 +38,19 @@ from .services.permission import PermissionService
|
|
|
35
38
|
from .services.redis import RedisService
|
|
36
39
|
from .services.role import RoleService
|
|
37
40
|
from .utils.config_loader import load_config
|
|
41
|
+
from .utils.error_utils import handle_api_error_snake_case, transform_error_to_snake_case
|
|
42
|
+
from .utils.filter import apply_filters, build_query_string, parse_filter_params
|
|
38
43
|
from .utils.http_client import HttpClient
|
|
39
44
|
from .utils.internal_http_client import InternalHttpClient
|
|
45
|
+
from .utils.pagination import (
|
|
46
|
+
apply_pagination_to_array,
|
|
47
|
+
create_meta_object,
|
|
48
|
+
create_paginated_list_response,
|
|
49
|
+
parse_pagination_params,
|
|
50
|
+
)
|
|
51
|
+
from .utils.sort import build_sort_string, parse_sort_params
|
|
40
52
|
|
|
41
|
-
__version__ = "0.
|
|
53
|
+
__version__ = "0.5.0"
|
|
42
54
|
__author__ = "AI Fabrix Team"
|
|
43
55
|
__license__ = "MIT"
|
|
44
56
|
|
|
@@ -491,6 +503,32 @@ __all__ = [
|
|
|
491
503
|
"PerformanceMetrics",
|
|
492
504
|
"ClientLoggingOptions",
|
|
493
505
|
"ErrorResponse",
|
|
506
|
+
# Pagination models
|
|
507
|
+
"Meta",
|
|
508
|
+
"PaginatedListResponse",
|
|
509
|
+
# Filter models
|
|
510
|
+
"FilterOperator",
|
|
511
|
+
"FilterOption",
|
|
512
|
+
"FilterQuery",
|
|
513
|
+
"FilterBuilder",
|
|
514
|
+
# Sort models
|
|
515
|
+
"SortOption",
|
|
516
|
+
# Pagination utilities
|
|
517
|
+
"parse_pagination_params",
|
|
518
|
+
"create_meta_object",
|
|
519
|
+
"apply_pagination_to_array",
|
|
520
|
+
"create_paginated_list_response",
|
|
521
|
+
# Filter utilities
|
|
522
|
+
"parse_filter_params",
|
|
523
|
+
"build_query_string",
|
|
524
|
+
"apply_filters",
|
|
525
|
+
# Sort utilities
|
|
526
|
+
"parse_sort_params",
|
|
527
|
+
"build_sort_string",
|
|
528
|
+
# Error utilities
|
|
529
|
+
"transform_error_to_snake_case",
|
|
530
|
+
"handle_api_error_snake_case",
|
|
531
|
+
# Services
|
|
494
532
|
"AuthService",
|
|
495
533
|
"RoleService",
|
|
496
534
|
"PermissionService",
|
|
@@ -33,9 +33,18 @@ class ErrorResponse(BaseModel):
|
|
|
33
33
|
|
|
34
34
|
errors: List[str] = Field(..., description="List of error messages")
|
|
35
35
|
type: str = Field(..., description="Error type URI (e.g., '/Errors/Bad Input')")
|
|
36
|
-
title: str = Field(
|
|
36
|
+
title: Optional[str] = Field(default=None, description="Human-readable error title")
|
|
37
37
|
statusCode: int = Field(..., alias="status_code", description="HTTP status code")
|
|
38
38
|
instance: Optional[str] = Field(default=None, description="Request instance URI")
|
|
39
|
+
request_key: Optional[str] = Field(
|
|
40
|
+
default=None, alias="requestKey", description="Request key for error tracking"
|
|
41
|
+
)
|
|
39
42
|
|
|
40
43
|
class Config:
|
|
41
44
|
populate_by_name = True # Allow both camelCase and snake_case
|
|
45
|
+
|
|
46
|
+
# Support snake_case attribute access
|
|
47
|
+
@property
|
|
48
|
+
def status_code(self) -> int:
|
|
49
|
+
"""Get statusCode as status_code (snake_case)."""
|
|
50
|
+
return self.statusCode
|