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)
Esempio n. 2
0
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
Esempio n. 3
0
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"]
Esempio n. 4
0
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
Esempio n. 5
0
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())
Esempio n. 6
0
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
Esempio n. 7
0
    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"}
        )
Esempio n. 8
0
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
Esempio n. 10
0
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)
Esempio n. 12
0
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)
Esempio n. 13
0
    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
Esempio n. 14
0
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-')
Esempio n. 15
0
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')
Esempio n. 16
0
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)
Esempio n. 17
0
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
Esempio n. 18
0
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)
Esempio n. 19
0
 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
Esempio n. 20
0
    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)
Esempio n. 21
0
    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
Esempio n. 22
0
 def query_params(self) -> QueryParams:
     if not hasattr(self, "_query_params"):
         self._query_params = QueryParams(self._scope["query_string"])
     return self._query_params
Esempio n. 23
0
 def query_params(self) -> QueryParams:
     if not hasattr(self, "_query_params"):
         self._query_params = QueryParams(scope=self._scope)
     return self._query_params
Esempio n. 24
0
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
Esempio n. 25
0
 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
Esempio n. 26
0
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
Esempio n. 27
0
    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"),
                })
Esempio n. 28
0
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() == {}
Esempio n. 29
0
 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
Esempio n. 30
0
 def url_params_update(init: QueryParams, **new: typing.Any) -> QueryParams:
     values = dict(init)
     values.update(new)
     return QueryParams(**values)