async def _get_response(self, request: Request, data: QueryParams) -> Response: try: query = data["query"] except KeyError: return PlainTextResponse("No GraphQL query found in the request", 400) config = get_graphql_config(request) background = BackgroundTasks() context = {"req": request, "background": background, **config.context} engine: Engine = config.engine result: dict = await engine.execute( query, context=context, variables=data.get("variables"), operation_name=data.get("operationName"), ) content = {"data": result["data"]} has_errors = "errors" in result if has_errors: content["errors"] = format_errors(result["errors"]) status = 400 if has_errors else 200 return JSONResponse(content=content, status_code=status, background=background)
def get_page_number(url: URL) -> int: """ Return a page number specified in the URL query parameters. """ query_params = QueryParams(url.query) try: return int(query_params.get("page", default="1")) except (TypeError, ValueError): return 1
def test_url_blank_params(): q = QueryParams("a=123&abc&def&b=456") assert "a" in q assert "abc" in q assert "def" in q assert "b" in q assert len(q.get("abc")) == 0 assert len(q["a"]) == 3 assert list(q.keys()) == ["a", "abc", "def", "b"]
def get_ordering(url: URL, columns: typing.Dict[str, str]) -> typing.Optional[str]: """ Determine a column ordering based on the URL query string. """ query_params = QueryParams(url.query) order_by = query_params.get("order") if order_by is not None and order_by.lstrip("-") not in columns: return None return order_by
async def get_logs(request): owner = request.path_params["owner"] project = request.path_params["project"] run_uuid = request.path_params["run_uuid"] force = to_bool(request.query_params.get("force"), handle_none=True) resource_name = get_resource_name(run_uuid=run_uuid) operation = get_run_instance(owner=owner, project=project, run_uuid=run_uuid) last_time = QueryParams(request.url.query).get("last_time") if last_time: last_time = dt_parser.parse(last_time).astimezone() last_file = QueryParams(request.url.query).get("last_file") k8s_manager = None k8s_operation = None if not last_file: k8s_manager = AsyncK8SManager( namespace=settings.CLIENT_CONFIG.namespace, in_cluster=settings.CLIENT_CONFIG.in_cluster, ) await k8s_manager.setup() k8s_operation = await get_k8s_operation(k8s_manager=k8s_manager, resource_name=resource_name) if not last_file and k8s_operation: last_file = None operation_logs, last_time = await get_k8s_operation_logs( operation=operation, last_time=last_time, k8s_manager=k8s_manager, stream=True, ) if k8s_operation["status"].get("completionTime"): last_time = None elif last_time: # Streaming should stop last_file = None last_time = None operation_logs = [] else: last_time = None operation_logs, last_file = await get_archived_operation_logs( run_uuid=run_uuid, last_file=last_file, check_cache=not force) if k8s_manager: await k8s_manager.close() response = V1Logs(last_time=last_time, last_file=last_file, logs=operation_logs) return UJSONResponse(response.to_dict())
def get_ordering( url: URL, columns: typing.Dict[str, str]) -> typing.Tuple[typing.Optional[str], bool]: """ Determine a column ordering based on the URL query string. Returned as a tuple of (ordering, is_reverse). """ query_params = QueryParams(url.query) ordering = query_params.get("order", default="") order_column = ordering.lstrip("-") order_reverse = ordering.startswith("-") if order_column not in columns: return None, False return order_column, order_reverse
def test_parsing(self): app = PiccoloCRUD(table=Movie) parsed_1 = app._parse_params( QueryParams("tags=horror&tags=scifi&rating=90") ) self.assertEqual( parsed_1, {"tags": ["horror", "scifi"], "rating": "90"} ) parsed_2 = app._parse_params( QueryParams("tags[]=horror&tags[]=scifi&rating=90") ) self.assertEqual( parsed_2, {"tags": ["horror", "scifi"], "rating": "90"} )
async def do_some_math(ws: WebSocket): await ws.accept() query_args = QueryParams(ws.scope["query_string"]) if 'mod' in query_args: mod = int(query_args['mod']) else: mod = None v = ws.scope['path_params']['a'] while True: if mod: v %= mod await ws.send_json(v) operation = await ws.receive_json() action = operation.get('op') if not action: return WS_1008_POLICY_VIOLATION if action == 'done': return WS_1000_NORMAL_CLOSURE operand = operation.get('value') if operand is None: return WS_1008_POLICY_VIOLATION if action == 'add': v += operand continue if action == 'mul': v *= operand continue
def from_query_params(cls, query: QueryParams): """ Парсит набор query параметров и возвращает FilterBy либо None. """ for k, v in query.items(): if k.startswith('filter'): match = re.match(r'filter\[(.+)\]', k) if match: return cls.construct(attr=match[1], value=v) return None
async def get_logs(request: Request) -> UJSONResponse: run_uuid = request.path_params["run_uuid"] force = to_bool(request.query_params.get("force"), handle_none=True) last_time = QueryParams(request.url.query).get("last_time") if last_time: last_time = parse_datetime(last_time).astimezone() last_file = QueryParams(request.url.query).get("last_file") files = [] if last_time: resource_name = get_resource_name(run_uuid=run_uuid) k8s_manager = AsyncK8SManager( namespace=settings.CLIENT_CONFIG.namespace, in_cluster=settings.CLIENT_CONFIG.in_cluster, ) await k8s_manager.setup() k8s_operation = await get_k8s_operation( k8s_manager=k8s_manager, resource_name=resource_name ) if k8s_operation: operation_logs, last_time = await get_operation_logs( k8s_manager=k8s_manager, k8s_operation=k8s_operation, instance=run_uuid, last_time=last_time, ) else: operation_logs, last_time = await get_tmp_operation_logs( run_uuid=run_uuid, last_time=last_time ) if k8s_manager: await k8s_manager.close() else: operation_logs, last_file, files = await get_archived_operation_logs( run_uuid=run_uuid, last_file=last_file, check_cache=not force ) response = V1Logs( last_time=last_time, last_file=last_file, logs=operation_logs, files=files ) return UJSONResponse(response.to_dict())
async def uppercase_params(request: Request, call_next): query_params = request.query_params query_params_upper = QueryParams( {k.upper(): v for k, v in query_params.items()}) # only scope/state are carried through request.scope["query_string"] = bytes(str(query_params_upper), "ascii") return await call_next(request)
def test_get_phrase_from_category(): """Test: get(self, params: QueryParams = None) -> List[Phrase] Phrase from specified category.""" service = PhraseService( settings['db_name'], settings['cln_name'] ) params = QueryParams({ 'category.id': 1 }) result = service.get(params) assert 1 == len(result)
def get(self, params: QueryParams = None) -> List[Phrase]: """Gets a list of all phrases.""" # https://developerslogblog.wordpress.com/2019/10/15/mongodb-how-to-filter-by-multiple-fields/ result = [] criteria = {} if params: if 'random' in params: val = params['random'] val = int(val) if val.isdigit() else 1 if val < 1: val = 1 pipeline = [] # Выбрать случайную цитату из категории. if 'category.id' in params: id = params['category.id'] if id.isdigit(): id = int(id) if id > 0: pipeline.append({'$match': {'category.id': id}}) pipeline.append({'$sample': {'size': val}}) cursor = self.phrases.aggregate(pipeline) else: p_fields = Phrase.__fields__.keys() params = params.__str__().split('&') params = [p.split('=') for p in params] for param in params: if param[0].startswith('category.'): c_fields = Category.__fields__.keys() kv = param[0].split('.') if kv[1] in c_fields: val = param[1] if val.isdigit(): val = int(val) if kv[1] == 'id' and val < 1: continue criteria[f'{param[0]}'] = val else: if param[0] in p_fields: val = param[1] if val.isdigit(): val = int(val) criteria[f'{param[0]}'] = val cursor = self.phrases.find(criteria) else: cursor = self.phrases.find() for found in cursor: phrase = Phrase(**found) result.append(phrase) return result
def test_get_random_phrase_less_one(): """Test: get(self, params: QueryParams = None) -> List[Phrase] Random phrase. Query param less then 1""" service = PhraseService( settings['db_name'], settings['cln_name'] ) params = QueryParams({ 'random': 0 }) result = service.get(params) assert 1 == len(result) assert result[0].author.startswith('test-author-')
def test_get_phrases_by_id(): """Test: get(self, params: QueryParams = None) -> List[Phrase] Returns list of phrases by id.""" service = PhraseService( settings['db_name'], settings['cln_name'] ) params = QueryParams({ 'id': '1' }) result = service.get(params) assert 1 == len(result) assert result[0].author.startswith('test-author-1')
async def search(req: Request): context = {"request": req} context["user"] = req.session.get("user") resp = api.list_resources(req.query_params) # If json-req if req.query_params.get("fmt", "") == "json": return JSONResponse(resp) # If SAM-req. It only wants json-encoded id-lists if "ids" in req.query_params.getlist("view"): return JSONResponse(resp) # If errors, show error-page if resp.get("errors"): context["errors"] = resp.get("errors") return render("error.html", context) # enables (and resets) traversal if resp.get("size") > 1 and resp.get("result"): ses = req.session params = MultiDict(req.query_params) params.pop("start", None) cur_search = QueryParams(params).__str__() if ses.get("traverse") and cur_search == ses["traverse"].get( "cur_search"): pass else: ses["traverse"] = {} ses["traverse"]["total"] = resp.get("total") ses["traverse"]["size"] = resp.get("size") ses["traverse"]["cur_search"] = cur_search ses["traverse"]["batches"] = {} start = str(resp.get("start", 0)) ses["traverse"]["start"] = int(start) if start not in ses["traverse"]["batches"]: ses["traverse"]["batches"][start] = [ int(d.get("id")) for d in resp.get("result") ] ses["traverse"]["cur_ids"] = ses["traverse"]["batches"].get(start) context.update(resp) return render("search.html", context)
async def get_auth_request(method: str, user_id: Optional[str] = None) -> Request: """Create test request.""" request_scope = { "type": "http", "method": method, "query_params": QueryParams(code="test"), "query_string": b"code=test", "headers": [], } if user_id: request_scope["user_id"] = user_id request = Request(scope=request_scope) return request
def test_get_phrases_by_author(): """Test: get(self, params: QueryParams = None) -> List[Phrase] Returns list of phrases by author.""" service = PhraseService( settings['db_name'], settings['cln_name'] ) service.create(data={ 'author': 'test-author-1', 'text': 'test-text-2' }) params = QueryParams({ 'author': 'test-author-1' }) result = service.get(params) assert 2 == len(result)
def __init__( self, http: _BaseClient, path: str = "", headers: Optional[MutableHeaders] = None, queries: Optional[Params] = None, cookies: Optional[Mapping] = None, timeout: Optional[float] = None, ): self._app_ref = AppRef(app=http._app_ref["app"]) self.host = http.host self._client = http._client self.path = path self.headers = headers or MutableHeaders() cookie_headers = SimpleCookie(cookies).output().splitlines() for c in cookie_headers: k, v = c.split(":", 1) self.headers.append(k, v) self.queries = QueryParams(queries or []) self.timeout = timeout
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: # important! not intercept other than FHIR service. if scope["type"] != "http" or "/fhir/" not in scope.get( "path", ""): # pragma: no cover await self.app(scope, receive, send) return params = QueryParams(scope.get("query_string", b"")) headers = Headers(scope=scope) errors = list() FHIRHTTPRequestHandlerMiddleware.prepare_fhir_scopes( scope, headers, params, errors) if len(errors) > 0: response = FHIRHTTPRequestHandlerMiddleware.error_response( scope, errors) await response(scope, receive, send) else: await self.app(scope, receive, send)
def _parse_params(self, params: QueryParams) -> t.Dict[str, t.Any]: """ The GET params may contain multiple values for each parameter name. For example: /tables/movie?tag=horror&tag=scifi Some clients, such as Axios, will use this convention: /tables/movie?tag[]=horror&tag[]=scifi This method normalises the parameter name, removing square brackets if present (tag[] -> tag), and will return a list of values if multiple are present. """ params_map: t.Dict[str, t.Any] = { i[0]: [j[1] for j in i[1]] for i in itertools.groupby(params.multi_items(), lambda x: x[0]) } array_columns = [ i._meta.name for i in self.table._meta.columns if i.value_type == list ] output = {} for key, value in params_map.items(): if key.endswith("[]") or key.rstrip("[]") in array_columns: # Is either an array, or multiple values have been passed in # for another field. key = key.rstrip("[]") elif len(value) == 1: value = value[0] output[key] = value return output
def query_params(self) -> QueryParams: if not hasattr(self, "_query_params"): self._query_params = QueryParams(self._scope["query_string"]) return self._query_params
def query_params(self) -> QueryParams: if not hasattr(self, "_query_params"): self._query_params = QueryParams(scope=self._scope) return self._query_params
def test_queryparams(): q = QueryParams("a=123&a=456&b=789") assert "a" in q assert "A" not in q assert "c" not in q assert q["a"] == "456" assert q.get("a") == "456" assert q.get("nope", default=None) is None assert q.getlist("a") == ["123", "456"] assert list(q.keys()) == ["a", "b"] assert list(q.values()) == ["456", "789"] assert list(q.items()) == [("a", "456"), ("b", "789")] assert len(q) == 2 assert list(q) == ["a", "b"] assert dict(q) == {"a": "456", "b": "789"} assert str(q) == "a=123&a=456&b=789" assert repr(q) == "QueryParams('a=123&a=456&b=789')" assert QueryParams({ "a": "123", "b": "456" }) == QueryParams([("a", "123"), ("b", "456")]) assert QueryParams({"a": "123", "b": "456"}) == QueryParams("a=123&b=456") assert QueryParams({ "a": "123", "b": "456" }) == QueryParams({ "b": "456", "a": "123" }) assert QueryParams() == QueryParams({}) assert QueryParams([("a", "123"), ("a", "456")]) == QueryParams("a=123&a=456") assert QueryParams({"a": "123", "b": "456"}) != "invalid" q = QueryParams([("a", "123"), ("a", "456")]) assert QueryParams(q) == q
def queries(self) -> QueryParams: """(`QueryParams`): The parsed query parameters used for the request.""" if self._queries is None: self._queries = QueryParams(self.url.query) return self._queries
def list_resources(query_params: QueryParams = None): def _validate_query_params(query_params): errors = [] stripped_keys = [] for key in query_params: negated = key[0] == "-" stripped_key = key[1:] if negated else key if stripped_key not in settings.QUERY_PARAMS: errors.append({"param": key, "msg": "Invalid query-param"}) continue # no further tests needed if negated and not settings.QUERY_PARAMS[stripped_key].get( "negatable" ): errors.append({"param": key, "msg": "Param not negatable"}) # Check if non-repeatable stripped_key already exists # eg. when using "-usability" and "usability" if stripped_key in stripped_keys and not settings.QUERY_PARAMS[ stripped_key ].get("repeatable"): errors.append({"param": key, "msg": "Param not repeatable"}) stripped_keys.append(stripped_key) # When all stripped_keys are iterated, test for series without collections. if "series" in stripped_keys and "collection" not in stripped_keys: errors.append( { "param": "series", "msg": "'Series'-key requires a 'collection'-key", } ) return {"errors": errors} def _urlencode(params=None, remove=None, insert=None): temp_params = params[:] if params else [] if insert: temp_params.append(insert) if remove and temp_params and (remove in temp_params): temp_params.remove(remove) return urlencode(temp_params) def _generate_filters(filters, params): # Adds links and creator-bools to filters out = [] for f in filters: el = {} key = f.get("key") value = f.get("value") negated = f.get("negated") # get label if unresolved if f.get("unresolved"): r = get_entity_labels([(key, value)]) if not r.get("errors"): el["label"] = r["data"][0].get("label") # View-link # 'label' indicates an id-based filter which can be viewed if el.get("label"): if key == "collection": el["view_link"] = "/".join(["collections", value]) else: el["view_link"] = "/".join([key, value]) # Remove_link # If positive collection, also remove series # negative collection-params works like normal param if key == "collection" and not negated: new_params = [ (k, v) for k, v in params if k not in ["collection", "series", "start"] ] el["remove_link"] = urlencode(new_params) else: new_params = [(k, v) for k, v in params if k not in ["start"]] org_key = "-" + key if negated else key el["remove_link"] = _urlencode( new_params, remove=(org_key, value) ) # Inverse_link # If negated, replace with positive, vice versa # exception: if positive collection, remove series-param, as # it follows the collection if negated: new_params = [(k, v) for k, v in params if k not in ["start"]] el["invert_link"] = _urlencode( new_params, remove=("-" + key, value), insert=(key, value) ) else: if key == "collection": new_params = [ (k, v) for k, v in params if k not in ["collection", "series", "start"] ] el["invert_link"] = _urlencode( new_params, insert=("-" + key, value) ) else: new_params = [ (k, v) for k, v in params if k not in ["start"] ] el["invert_link"] = _urlencode( new_params, insert=("-" + key, value), remove=(key, value), ) # Creator and collector links and bools if key in ["people", "organisations"]: r = get_resource(key, int(value)) if not r.get("errors"): if r["data"].get("is_creative_creator"): el["creator_link"] = "creators=" + value el["creator"] = True if r["data"].get("is_creator"): el["creator_link"] = "collectors=" + value el["collector"] = True el["negated"] = negated el["key"] = key el["value"] = value out.append(el) return out def _generate_facets(facets, params=None): # TODO: Does not work when excisting negative filter is set # and you click a positive facet: '-usability=4' is set, you click 'usability=2' result = {} params = [ x for x in params if x[0] != "start" ] # remove 'start'-param from facet-links for facet in facets: out = {} for b in facets[facet].get("buckets"): active = (facet, b.get("value")) # tuple if params and (active in params): # if stripped_params = [x for x in params if x != active] b["remove_link"] = urlencode(stripped_params) elif params: b["add_link"] = urlencode(params + [active]) else: b["add_link"] = urlencode([active]) out[b.get("value")] = b result[facet] = out return result def _generate_total_facets(aws_facets: Dict, params: List = None) -> Dict: # TODO: Does not work when excisting negative filter is set # and you click a positive facet: '-usability=4' is set, you click 'usability=2' def _recurse( facet_key: str, total_facet_values: List, active_facet_values: Dict, params: List = None, ) -> List: # total_facet_values is the "content"-key from each facet in settings.FACETS # active_facet_values is a dict of facet-values and facetinfo, e.g. # {"1": {"value": "1", "count": 63, "link": "add_or_remove_link_url"}} out = [] for facet in total_facet_values: id_ = facet.get("id") label = facet.get("label") if id_ in active_facet_values: current_tuple = (facet_key, id_) el = { "label": label, "id": id_, "count": active_facet_values[id_].get("count"), } if params and ( current_tuple in params ): # if the current tuple is in the params stripped_params = [ x for x in params if x != current_tuple ] el["remove_link"] = urlencode(stripped_params) elif params: el["add_link"] = urlencode(params + [current_tuple]) else: el["add_link"] = urlencode([current_tuple]) if facet.get("children"): el["children"] = _recurse( facet_key, facet.get("children"), active_facet_values, params, ) out.append(el) return out # return list of dicts with label, link and children # restructure aws-facets active_facets = {} for facet in aws_facets: active_facets[facet] = { b.get("value"): b for b in aws_facets[facet].get("buckets") } # remove 'start'-param from facet-links, retain all else params = [x for x in params if x[0] != "start"] result = {} for k, v in settings.FACETS.items(): # label = facet.get("label") # recursively merge labels and links from total_facets and active_facets result[k] = _recurse( k, v.get("content"), active_facets.get(k), params ) collection_tuples = [ ("collection", key) for key in active_facets["collection"].keys() ] # result["collection_tuples"] = collection_tuples collection_labels = get_entity_labels(collection_tuples) # result["collection_labels"] = collection_labels result["collection"] = _recurse( "collection", collection_labels.get("data"), active_facets.get("collection"), params, ) result["collection_labels"] = collection_labels return result def _generate_collection_labels(col_labels): return {d_.get("id"): d_.get("label") for d_ in col_labels.get("data")} def _generate_content_labels(type_list): return {d_.get("id"): d_.get("label") for d_ in type_list} def _generate_views(params, view): output = [] views = [ { "label": "Listevisning", "value": "list", "icon": "fas fa-list", # 'view_list' }, { "label": "Galleri-visning", "value": "gallery", "icon": "fas fa-th", # 'view_module' }, ] if params: stripped_params = [(t[0], t[1]) for t in params if t[0] != "view"] else: stripped_params = [] for option in views: current = {} current["label"] = option.get("label") current["icon"] = option.get("icon") if option.get("value") == view: current["selected"] = True else: current["link"] = urlencode( stripped_params + [("view", option.get("value"))] ) output.append(current) return output def _generate_sorts(params, sort, direction): sorts = [ { "label": "Ældste dato først", "sort": "date_from", "direction": "asc", }, { "label": "Nyeste dato først", "sort": "date_to", "direction": "desc", }, {"label": "Relevans", "sort": "_score", "direction": "desc"}, ] output = [] if params: stripped_params = [ (t[0], t[1]) for t in params if t[0] not in ["sort", "direction", "start"] ] else: stripped_params = [] for option in sorts: current = {} current["icon"] = option.get("icon") current["label"] = option.get("label") if ( option.get("sort") == sort and option.get("direction") == direction ): current["selected"] = True else: current["link"] = urlencode( stripped_params + [ ("sort", option.get("sort")), ("direction", option.get("direction")), ] ) output.append(current) return output def _generate_sizes(params, size): sizes = [20, 50, 100] output = [] if params: stripped_params = [(t[0], t[1]) for t in params if t[0] != "size"] else: stripped_params = [] for option in sizes: current = {} current["label"] = option if option == size: current["selected"] = True else: current["link"] = urlencode( stripped_params + [("size", option)] ) output.append(current) return output # Validate params if query_params: validated_request = _validate_query_params(query_params) if validated_request.get("errors"): return validated_request # Make api-call api_resp = searchInterface.search_records(query_params) # If api-error if api_resp.get("errors"): return api_resp # If SAM-request, no need for further processing # api_resp on request with "view=ids"-param includes three keys: status_code, result, next_cursor (optional) if "ids" in query_params.getlist("view"): return api_resp # Else process and convert response # api_resp on normal request includes: sort, direction, size, date_from, # date_to, _query_string, total, start, server_facets, filters, query, result, # view_list, sort_list, size_list, view, non_query_params resp = {} # convert multidict to list of tuples # params = [tup for tup in query_params.items(multi=True)] # Flask-syntax params = [tup for tup in query_params.multi_items()] # starlette-syntax # Keys used for generating searchviews and facets resp["params"] = params resp["query"] = api_resp.get("query", None) # If filters, generate links and possibly labels if api_resp.get("filters"): resp["filters"] = _generate_filters(api_resp["filters"], params) # if facets, generate links if api_resp.get("facets"): resp["facets"] = _generate_total_facets(api_resp["facets"], params) # Generate collection-labels col_labels = resp["facets"].pop("collection_labels") if col_labels: resp["collection_labels"] = _generate_collection_labels(col_labels) # Generate content_types-labels # content_labels = resp["facets"].pop("content_labels") # if content_labels: resp["content_labels"] = _generate_content_labels( resp["facets"].get("content_types") ) # 'non_query_params' is used to generate a remove_link for the q-param # on the zero-hits page if not api_resp.get("result") and api_resp.get("query"): other_params = [i for i in params if i != ("q", api_resp.get("query"))] resp["non_query_params"] = urlencode(other_params) # Pagination if api_resp.get("result"): total = api_resp["total"] start = api_resp["start"] size = api_resp["size"] rm_tup = ("start", str(start)) if start > 0: resp["first"] = _urlencode(params, remove=rm_tup) resp["prev"] = _urlencode( params, remove=rm_tup, insert=("start", start - size) ) if total <= 10000 and (start + size < total): last_start = total // size * size if last_start == total: last_start = total - size resp["last"] = _urlencode( params, remove=rm_tup, insert=("start", last_start) ) if (start + size < total) and (start + size <= 10000): resp["next"] = _urlencode( params, remove=rm_tup, insert=("start", start + size) ) # Proces size, sort, direction and view resp["size_list"] = _generate_sizes(params, api_resp["size"]) resp["sort_list"] = _generate_sorts( params, api_resp["sort"], api_resp["direction"] ) resp["view_list"] = _generate_views( params, query_params.get("view", "list") ) resp["view"] = query_params.get("view", "list") resp["total"] = api_resp.get("total") resp["start"] = api_resp.get("start") resp["size"] = api_resp.get("size") resp["sort"] = api_resp.get("sort") resp["date_from"] = api_resp.get("date_from") resp["date_to"] = api_resp.get("date_to") resp["result"] = api_resp.get("result") # resp["api_response"] = api_resp.get("api_response") return resp
def prepare_fhir_scopes( scope: Scope, headers: Headers, params: QueryParams, errors: typing.List[typing.Any], ): # Generate Request ID scope["FHIR_REQUEST_ID"] = uuid.uuid4() # 1. Prepare Accept & FHIR Version # -------------------------------- accept = headers.get("accept", None) if accept: parts = accept.split(";") accept = parts[0].strip() if accept in ("application/json", "text/json"): accept = "application/fhir+json" if accept in ALLOWED_ACCEPTS: scope["FHIR_RESPONSE_ACCEPT"] = accept else: errors.append({ "loc": "Header.Accept", "msg": f"Accept mime '{accept}' is not supported.", "original": headers.get("accept"), }) if len(parts) > 1: version_str = None try: name, version_str = parts[1].strip().split("=") if name == "fhirVersion": version = MIME_FHIR_VERSION_MAP[version_str] scope["FHIR_VERSION"] = version scope["FHIR_VERSION_ORIGINAL"] = version_str else: errors.append({ "loc": "Header.Accept", "msg": "Invalid format of FHIR Version is provided in mime", "original": headers.get("accept"), }) except KeyError: errors.append({ "loc": "Header.Accept", "msg": f"Unsupported FHIR Version '{version_str}' is provided in mime", "original": headers.get("accept"), }) except ValueError: errors.append({ "loc": "Header.Accept", "msg": "Invalid format of FHIR Version is provided in mime", "original": headers.get("accept"), }) else: scope["FHIR_RESPONSE_ACCEPT"] = "application/fhir+json" if (scope.get("FHIR_VERSION_ORIGINAL", None) is None and scope.get("FHIR_VERSION", None) is None): scope["FHIR_VERSION"] = MIME_FHIR_VERSION_MAP[DEFAULT_FHIR_VERSION] # 2. Check Query String # --------------------- format_mime = params.get("_format", None) if format_mime is not None: if format_mime in ALLOWED_ACCEPTS: scope["FHIR_RESPONSE_FORMAT"] = format_mime else: errors.append({ "loc": "QueryString._format", "msg": f"Format mime '{format_mime}' is not supported.", "original": format_mime, }) pretty_response = params.get("_pretty", None) if pretty_response is not None: if pretty_response in ("true", "false"): scope["FHIR_RESPONSE_PRETTY"] = pretty_response == "true" else: errors.append({ "loc": "QueryString._pretty", "msg": f"Invalid ``_pretty`` value '{pretty_response}' is provided.", "original": pretty_response, }) # 3. Prepare Conditional Headers # ------------------------------ if headers.get("If-None-Exist"): scope["FHIR_CONDITION_NONE_EXIST"] = [ tuple( map(lambda x: x.strip(), headers.get("If-None-Exist").split("="))) ] if headers.get("If-Modified-Since"): try: scope["FHIR_CONDITION_MODIFIED_SINCE"] = parsedate_to_datetime( headers.get("If-Modified-Since")) except ValueError: errors.append({ "loc": "Header.If-Modified-Since", "msg": "Invalid formatted datetime value is provided.", "original": headers.get("If-Modified-Since"), }) if headers.get("If-None-Match"): try: scope["FHIR_CONDITION_NONE_MATCH"] = literal_eval( headers.get("If-None-Match").replace("W/", "")) except (SyntaxError, ValueError): errors.append({ "loc": "Header.If-None-Match", "msg": "Invalid formatted ETag value is provided.", "original": headers.get("If-None-Match"), }) if headers.get("If-Match"): try: scope["FHIR_CONDITION_MATCH"] = literal_eval( headers.get("If-Match").replace("W/", "")) except (SyntaxError, ValueError): errors.append({ "loc": "Header.If-Match", "msg": "Invalid formatted ETag value is provided.", "original": headers.get("If-Match"), })
def test_queryparams(): q = QueryParams([("a", "123"), ("a", "456"), ("b", "789")]) assert "a" in q assert "A" not in q assert "c" not in q assert q["a"] == "123" assert q.get("a") == "123" assert q.get("nope", default=None) is None assert q.getlist("a") == ["123", "456"] assert q.keys() == ["a", "a", "b"] assert q.values() == ["123", "456", "789"] assert q.items() == [("a", "123"), ("a", "456"), ("b", "789")] assert list(q) == [("a", "123"), ("a", "456"), ("b", "789")] assert dict(q) == {"a": "123", "b": "789"} assert repr(q) == "QueryParams([('a', '123'), ('a', '456'), ('b', '789')])" assert QueryParams({ "a": "123", "b": "456" }) == QueryParams([("a", "123"), ("b", "456")]) assert QueryParams({"a": "123", "b": "456"}) == {"b": "456", "a": "123"} assert QueryParams({ "a": "123", "b": "456" }) == [("b", "456"), ("a", "123")] assert {"b": "456", "a": "123"} == QueryParams({"a": "123", "b": "456"}) assert [("b", "456"), ("a", "123")] == QueryParams({ "a": "123", "b": "456" }) assert QueryParams() == {}
def query_params(self) -> QueryParams: if not hasattr(self, "_query_params"): query_string = self._scope["query_string"].decode() self._query_params = QueryParams(query_string) return self._query_params
def url_params_update(init: QueryParams, **new: typing.Any) -> QueryParams: values = dict(init) values.update(new) return QueryParams(**values)