Skip to content

Commit

Permalink
Implement list_observations API endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
hellais committed Jan 8, 2024
1 parent 740197b commit d64463f
Showing 1 changed file with 143 additions and 0 deletions.
143 changes: 143 additions & 0 deletions oonidata/fastapi/routers/observations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import dataclasses
from datetime import date
import math
from typing import List, Literal, Optional, Union
from fastapi import APIRouter, Depends, Query
from pydantic import BaseModel
from typing_extensions import Annotated

Check warning on line 7 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L1-L7

Added lines #L1 - L7 were not covered by tests

from oonidata.datautils import PerfTimer
from oonidata.models.observations import WebObservation

Check warning on line 10 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L9-L10

Added lines #L9 - L10 were not covered by tests

from ..config import settings
from ..dependencies import ClickhouseClient, get_clickhouse_client

Check warning on line 13 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L12-L13

Added lines #L12 - L13 were not covered by tests

router = APIRouter()

Check warning on line 15 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L15

Added line #L15 was not covered by tests


class ResponseMetadata(BaseModel):
count: int
current_page: int
limit: int
next_url: str
offset: int
pages: int
query_time: float

Check warning on line 25 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L18-L25

Added lines #L18 - L25 were not covered by tests


class WebObservationEntry(BaseModel):
pass

Check warning on line 29 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L28-L29

Added lines #L28 - L29 were not covered by tests


ObservationEntry = Union[WebObservationEntry, BaseModel]

Check warning on line 32 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L32

Added line #L32 was not covered by tests

# TODO: dynamically create the WebObservation entry class from this:
# for x in dataclasses.fields(WebObservation):
# setattr(WebObservation, x.name, x.type)


class ListObservationsResponse(BaseModel):
metadata: ResponseMetadata
results: List[ObservationEntry]

Check warning on line 41 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L39-L41

Added lines #L39 - L41 were not covered by tests


@router.get("/observations", tags=["observations"])
async def list_observations(

Check warning on line 45 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L44-L45

Added lines #L44 - L45 were not covered by tests
db: Annotated[ClickhouseClient, Depends(get_clickhouse_client)],
report_id: Annotated[Optional[str], Query()] = None,
probe_asn: Annotated[Union[int, str, None], Query()] = None,
probe_cc: Annotated[Optional[str], Query(max_length=2, min_length=2)] = None,
test_name: Annotated[Optional[str], Query()] = None,
since: Annotated[Optional[date], Query()] = None,
until: Annotated[Optional[date], Query()] = None,
order_by: Annotated[
Literal[
"measurement_start_time",
"input",
"probe_cc",
"probe_asn",
"test_name",
],
Query(),
] = "measurement_start_time",
order: Annotated[Optional[Literal["asc", "desc", "ASC", "DESC"]], Query()] = "DESC",
offset: Annotated[int, Query()] = 0,
limit: Annotated[int, Query()] = 100,
software_name: Annotated[Optional[str], Query()] = None,
software_version: Annotated[Optional[str], Query()] = None,
test_version: Annotated[Optional[str], Query()] = None,
engine_version: Annotated[Optional[str], Query()] = None,
ooni_run_link_id: Annotated[Optional[str], Query()] = None,
) -> ListObservationsResponse:
q_args = {}
and_clauses = []
if report_id is not None:
q_args["report_id"] = report_id
and_clauses.append("report_id = %(report_id)s")
if probe_asn is not None:
if isinstance(probe_asn, str) and probe_asn.startswith("AS"):
probe_asn = int(probe_asn[2:])
q_args["probe_asn"] = probe_asn
and_clauses.append("probe_asn = %(probe_asn)d")
if probe_cc is not None:
q_args["probe_cc"] = probe_cc
and_clauses.append("probe_cc = %(probe_cc)s")

Check warning on line 84 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L72-L84

Added lines #L72 - L84 were not covered by tests

if software_name is not None:
q_args["software_name"] = software_version
and_clauses.append("software_name = %(software_name)s")
if software_version is not None:
q_args["software_version"] = software_version
and_clauses.append("software_version = %(software_version)s")

Check warning on line 91 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L86-L91

Added lines #L86 - L91 were not covered by tests

if test_name is not None:
q_args["test_name"] = test_name
and_clauses.append("test_name = %(test_name)s")
if test_version is not None:
q_args["test_version"] = test_version
and_clauses.append("test_version = %(test_version)s")
if engine_version is not None:
q_args["engine_version"] = engine_version
and_clauses.append("engine_version = %(engine_version)s")

Check warning on line 101 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L93-L101

Added lines #L93 - L101 were not covered by tests

if ooni_run_link_id is not None:
q_args["ooni_run_link_id"] = ooni_run_link_id
and_clauses.append("ooni_run_link_id = %(ooni_run_link_id)s")

Check warning on line 105 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L103-L105

Added lines #L103 - L105 were not covered by tests

if since is not None:
q_args["since"] = since
and_clauses.append("measurement_start_time >= %(since)s")
if until is not None:
and_clauses.append("measurement_start_time <= %(until)s")
q_args["until"] = until

Check warning on line 112 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L107-L112

Added lines #L107 - L112 were not covered by tests

cols = [x.name for x in dataclasses.fields(WebObservation)]
q = f"SELECT {','.join(cols)} FROM web_observations"
if len(and_clauses) > 0:
q += " WHERE "
q += " AND ".join(and_clauses)
q += f" ORDER BY {order_by} {order} LIMIT {limit} OFFSET {offset}"

Check warning on line 119 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L114-L119

Added lines #L114 - L119 were not covered by tests

t = PerfTimer()
rows = db.execute(q, q_args)

Check warning on line 122 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L121-L122

Added lines #L121 - L122 were not covered by tests

results: List[ObservationEntry] = []
if rows and isinstance(rows, list):
for row in rows:
d = dict(zip(cols, row))
results.append(WebObservationEntry(**d))

Check warning on line 128 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L124-L128

Added lines #L124 - L128 were not covered by tests

response = ListObservationsResponse(

Check warning on line 130 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L130

Added line #L130 was not covered by tests
metadata=ResponseMetadata(
count=-1,
current_page=math.ceil(offset / limit) + 1,
limit=limit,
next_url=f"{settings.base_url}/api/v1/observations?offset={offset+limit}&limit={limit}",
offset=offset,
pages=-1,
query_time=t.s,
),
results=results,
)

return response

Check warning on line 143 in oonidata/fastapi/routers/observations.py

View check run for this annotation

Codecov / codecov/patch

oonidata/fastapi/routers/observations.py#L143

Added line #L143 was not covered by tests

0 comments on commit d64463f

Please sign in to comment.