88 List ,
99 Optional ,
1010 Type ,
11- Union ,
1211)
1312from urllib .parse import unquote
1413
2625from fastapi_jsonapi .api import RoutersJSONAPI
2726from fastapi_jsonapi .exceptions import (
2827 BadRequest ,
28+ InvalidField ,
2929 InvalidFilters ,
3030 InvalidInclude ,
3131 InvalidSort ,
32+ InvalidType ,
3233)
3334from fastapi_jsonapi .schema import (
3435 get_model_field ,
@@ -89,30 +90,45 @@ def __init__(self, request: Request) -> None:
8990 self .MAX_INCLUDE_DEPTH : int = self .config .get ("MAX_INCLUDE_DEPTH" , 3 )
9091 self .headers : HeadersQueryStringManager = HeadersQueryStringManager (** dict (self .request .headers ))
9192
92- def _get_key_values (self , name : str ) -> Dict [str , Union [List [str ], str ]]:
93+ def _extract_item_key (self , key : str ) -> str :
94+ try :
95+ key_start = key .index ("[" ) + 1
96+ key_end = key .index ("]" )
97+ return key [key_start :key_end ]
98+ except Exception :
99+ msg = "Parse error"
100+ raise BadRequest (msg , parameter = key )
101+
102+ def _get_unique_key_values (self , name : str ) -> Dict [str , str ]:
93103 """
94104 Return a dict containing key / values items for a given key, used for items like filters, page, etc.
95105
96106 :param name: name of the querystring parameter
97107 :return: a dict of key / values items
98108 :raises BadRequest: if an error occurred while parsing the querystring.
99109 """
100- results = defaultdict ( set )
110+ results = {}
101111
102112 for raw_key , value in self .qs .multi_items ():
103113 key = unquote (raw_key )
104- try :
105- if not key .startswith (name ):
106- continue
114+ if not key .startswith (name ):
115+ continue
107116
108- key_start = key .index ("[" ) + 1
109- key_end = key .index ("]" )
110- item_key = key [key_start :key_end ]
117+ item_key = self ._extract_item_key (key )
118+ results [item_key ] = value
111119
112- results [item_key ].update (value .split ("," ))
113- except Exception :
114- msg = "Parse error"
115- raise BadRequest (msg , parameter = key )
120+ return results
121+
122+ def _get_multiple_key_values (self , name : str ) -> Dict [str , List ]:
123+ results = defaultdict (list )
124+
125+ for raw_key , value in self .qs .multi_items ():
126+ key = unquote (raw_key )
127+ if not key .startswith (name ):
128+ continue
129+
130+ item_key = self ._extract_item_key (key )
131+ results [item_key ].extend (value .split ("," ))
116132
117133 return results
118134
@@ -131,7 +147,7 @@ def querystring(self) -> Dict[str, str]:
131147 return {
132148 key : value
133149 for (key , value ) in self .qs .multi_items ()
134- if key .startswith (self .managed_keys ) or self ._get_key_values ("filter[" )
150+ if key .startswith (self .managed_keys ) or self ._get_unique_key_values ("filter[" )
135151 }
136152
137153 @property
@@ -156,8 +172,8 @@ def filters(self) -> List[dict]:
156172 raise InvalidFilters (msg )
157173
158174 results .extend (loaded_filters )
159- if self ._get_key_values ("filter[" ):
160- results .extend (self ._simple_filters (self . _get_key_values ( "filter[" ) ))
175+ if filter_key_values := self ._get_unique_key_values ("filter[" ):
176+ results .extend (self ._simple_filters (filter_key_values ))
161177 return results
162178
163179 @cached_property
@@ -183,7 +199,7 @@ def pagination(self) -> PaginationQueryStringManager:
183199 :raises BadRequest: if the client is not allowed to disable pagination.
184200 """
185201 # check values type
186- pagination_data : Dict [str , Union [ List [ str ], str ]] = self ._get_key_values ("page" )
202+ pagination_data : Dict [str , str ] = self ._get_unique_key_values ("page" )
187203 pagination = PaginationQueryStringManager (** pagination_data )
188204 if pagination_data .get ("size" ) is None :
189205 pagination .size = None
@@ -213,23 +229,27 @@ def fields(self) -> Dict[str, List[str]]:
213229
214230 :raises InvalidField: if result field not in schema.
215231 """
216- fields = self ._get_key_values ("fields" )
232+ fields = self ._get_multiple_key_values ("fields" )
217233 for resource_type , field_names in fields .items ():
218234 # TODO: we have registry for models (BaseModel)
219235 # TODO: create `type to schemas` registry
220236
221- # schema: Type[BaseModel] = get_schema_from_type(key, self.app)
237+ if resource_type not in RoutersJSONAPI .all_jsonapi_routers :
238+ msg = f"Application has no resource with type { resource_type !r} "
239+ raise InvalidType (msg )
240+
241+ schema : Type [BaseModel ] = RoutersJSONAPI .all_jsonapi_routers [resource_type ]._schema
222242 self ._get_schema (resource_type )
223243
224- # for field_name in field_names:
225- # if field_name not in schema.__fields__:
226- # msg = "{schema} has no attribute {field}".format(
227- # schema=schema.__name__,
228- # field=field_name,
229- # )
230- # raise InvalidField(msg)
244+ for field_name in field_names :
245+ if field_name not in schema .__fields__ :
246+ msg = "{schema} has no attribute {field}" .format (
247+ schema = schema .__name__ ,
248+ field = field_name ,
249+ )
250+ raise InvalidField (msg )
231251
232- return fields
252+ return { resource_type : set ( field_names ) for resource_type , field_names in fields . items ()}
233253
234254 def _get_schema (self , resource_type : str ) -> Type [BaseModel ]:
235255 target_router = RoutersJSONAPI .all_jsonapi_routers [resource_type ]
0 commit comments