diff --git a/app/domain/dataapi/actions.py b/app/domain/dataapi/actions.py index d4ed75fc..2918ae9e 100644 --- a/app/domain/dataapi/actions.py +++ b/app/domain/dataapi/actions.py @@ -20,6 +20,7 @@ def __init__( catalog_cfg: responders.CatalogConfig, ) -> None: self.layer2_repo = layer2_repo + self.catalog_cfg = catalog_cfg self.parameterized_query_manager = parameterized_query.ParameterizedQueryManager( layer2_repo, ENABLED_CATALOGS, catalog_cfg ) @@ -33,8 +34,8 @@ def query(self, query: dataapi.QueryRequest) -> dataapi.QueryResponse: query.page_size, query.page, ) - responder = responders.JSONResponder() - pgc_objects = responder.build_response(objects) + responder = responders.StructuredResponder(self.catalog_cfg) + pgc_objects = responder.build_response(objects).objects return dataapi.QueryResponse(objects=pgc_objects) def query_fits(self, query: dataapi.FITSRequest) -> bytes: diff --git a/app/domain/responders/json_responder.py b/app/domain/responders/json_responder.py index 55b2f207..0aeeb26c 100644 --- a/app/domain/responders/json_responder.py +++ b/app/domain/responders/json_responder.py @@ -9,7 +9,7 @@ def objects_to_response(objects: list[model.Layer2Object]) -> list[dataapi.PGCOb response_objects = [] for obj in objects: catalog_data = {o.catalog().value: o.layer2_data() for o in obj.data} - response_objects.append(dataapi.PGCObject(obj.pgc, catalog_data)) + response_objects.append(dataapi.PGCObject(pgc=obj.pgc, catalogs=catalog_data)) return response_objects diff --git a/app/presentation/dataapi/server.py b/app/presentation/dataapi/server.py index 7f5a908e..8b85b1b3 100644 --- a/app/presentation/dataapi/server.py +++ b/app/presentation/dataapi/server.py @@ -11,6 +11,14 @@ logger = structlog.stdlib.get_logger() +def _query_request_dep( + q: Annotated[str, fastapi.Query(description="Query string")], + page_size: Annotated[int, fastapi.Query(description="Number of objects per page")] = 10, + page: Annotated[int, fastapi.Query(description="Page number")] = 0, +) -> interface.QueryRequest: + return interface.QueryRequest(q=q, page_size=page_size, page=page) + + class API: def __init__(self, actions: interface.Actions) -> None: self.actions = actions @@ -23,7 +31,8 @@ def query_simple( return server.APIOkResponse(data=response) def query( - self, request: Annotated[interface.QueryRequest, fastapi.Query()] + self, + request: Annotated[interface.QueryRequest, fastapi.Depends(_query_request_dep)], ) -> server.APIOkResponse[interface.QueryResponse]: response = self.actions.query(request) @@ -73,18 +82,13 @@ def __init__( http.HTTPMethod.GET, api.query, "Query data about objects using query string", - """Obtains objects using the query string. It is composed of functions and operators. - -Allowed functions are: -- `pgc`: Returns object with the particular PGC number. -- `name`: Returns objects that are sufficiently similar to the given name. -- `pos`: Returns objects that are within 1 arcsecond to the given coordinates. - -Allowed operators are: -- `and`: Logical AND operator. -- `or`: Logical OR operator. + """Obtains objects using a free-form query string. -Note that the answer is paginated to improve performance.""", +The string is interpreted as: +- A designation (name) search if it does not match a coordinate format; objects with matching designation are returned. +- Coordinates in HMS/DMS form (e.g. 12h30m49.32s+12d22m33.2s) or J form (e.g. J123049.32+122233.2); + objects within 1 arcsecond are returned. +If the string matches multiple interpretations, results are combined with OR.""", ), server.Route( "/v1/query/fits", diff --git a/tests/regression/upload_simple_table.py b/tests/regression/upload_simple_table.py index e09931f5..6dcc475d 100644 --- a/tests/regression/upload_simple_table.py +++ b/tests/regression/upload_simple_table.py @@ -360,6 +360,14 @@ def check_dataapi_coord_query(session: lib.TestSession, ra: float, dec: float, r assert len(coord_results["objects"]) > 0 +@lib.test_logging_decorator +def check_dataapi_query(session: lib.TestSession, q: str, page_size: int = 10, page: int = 0): + response = session.get("/v1/query", params={"q": q, "page_size": page_size, "page": page}) + response.raise_for_status() + data = response.json()["data"] + assert "objects" in data + + def get_adminapi_session() -> lib.TestSession: api_host = os.getenv("API_HOST", "localhost") api_port = os.getenv("API_PORT", "8080") @@ -409,6 +417,8 @@ def run(): check_dataapi_name_query(dataapi, "NGC") check_dataapi_coord_query(dataapi, COORD_RA_CENTER, COORD_DEC_CENTER, COORD_RADIUS / 2) + check_dataapi_query(dataapi, "NGC") + check_dataapi_query(dataapi, " ", page_size=10, page=0) # ---- Create table with all `existing` objects and upload it to layer 2 ---- table_name_2 = create_table(adminapi, code)