Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate /node/full/{path} routes #612

Merged
merged 6 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,13 @@ data in whole or in efficiently-chunked parts in the format of your choice:

```
# Download tabular data as CSV
http://localhost:8000/api/v1/node/full/long_table?format=csv
http://localhost:8000/api/v1/table/full/long_table?format=csv

# or XLSX (Excel)
http://localhost:8000/api/v1/node/full/long_table?format=xslx
http://localhost:8000/api/v1/table/full/long_table?format=xslx

# and subselect columns.
http://localhost:8000/api/v1/node/full/long_table?format=xslx&field=A&field=B
http://localhost:8000/api/v1/table/full/long_table?format=xslx&field=A&field=B
danielballan marked this conversation as resolved.
Show resolved Hide resolved

# View or download (2D) array data as PNG
http://localhost:8000/api/v1/array/full/medium_image?format=png
Expand Down
4 changes: 2 additions & 2 deletions docs/source/explanations/compression.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ the client lists it as one that it supports. Here, the client lists `zstd` and
`gzip`.

```
$ http -p Hh :8000/node/full/C accept-encoding:zstd,gzip
GET /node/full/C HTTP/1.1
$ http -p Hh :8000/table/full/C accept-encoding:zstd,gzip
GET /table/full/C HTTP/1.1
danielballan marked this conversation as resolved.
Show resolved Hide resolved
Accept: */*
Connection: keep-alive
Host: localhost:8000
Expand Down
14 changes: 7 additions & 7 deletions docs/source/explanations/specialized-formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ tiled catalog register catalog.db \
As is, we can access the data as CSV, for example.

```
$ curl -H 'Accept: text/csv' 'http://localhost:8000/api/v1/node/full/example'
$ curl -H 'Accept: text/csv' 'http://localhost:8000/api/v1/table/full/example'
,energy,i0,itrans,mutrans
0,8779.0,149013.7,550643.089065,-1.3070486
1,8789.0,144864.7,531876.119084,-1.3006104
Expand All @@ -135,20 +135,20 @@ There are three equivalent ways to request a format, more formally called a "med
1. Use the standard [HTTP `Accept` Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept).

```
$ curl -H 'Accept: text/csv' 'http://localhost:8000/api/v1/node/full/example'
$ curl -H 'Accept: text/csv' 'http://localhost:8000/api/v1/table/full/example'
```

2. Place the media type in a `format` query parameter.

```
$ curl 'http://localhost:8000/api/v1/node/full/example?format=text/csv'
$ curl 'http://localhost:8000/api/v1/table/full/example?format=text/csv'
```

3. Provide just a file extension. This is user friendly for people who do not know or care what
a "media type" is. The server looks up `csv` in a registry mapping file extensions to media types.

```
$ curl 'http://localhost:8000/api/v1/node/full/example?format=csv'
$ curl 'http://localhost:8000/api/v1/table/full/example?format=csv'
```
```

Expand Down Expand Up @@ -273,7 +273,7 @@ tiled serve config --public config.yml
we can request the content as XDI in any of these ways:

```
$ curl -H 'Accept: application/x-xdi' 'http://localhost:8000/api/v1/node/full/example.xdi'
$ curl 'http://localhost:8000/api/v1/node/full/example?format=application/x-xdi'
$ curl 'http://localhost:8000/api/v1/node/full/example?format=xdi'
$ curl -H 'Accept: application/x-xdi' 'http://localhost:8000/api/v1/table/full/example.xdi'
$ curl 'http://localhost:8000/api/v1/table/full/example?format=application/x-xdi'
$ curl 'http://localhost:8000/api/v1/table/full/example?format=xdi'
danielballan marked this conversation as resolved.
Show resolved Hide resolved
```
19 changes: 10 additions & 9 deletions docs/source/reference/http-api-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ entries.

The ``GET /api/v1/metadata/{path}`` route provides the metadata about one node.
The ``GET /api/v1/search/{path}`` route provides paginated access to the children of
a given node, with optional filtering (search). The ``GET /api/v1/node/full/{path}`` route
provides all the metadata and data below a given node.

Specialized data access routes ``GET /api/v1/array/block/{path}``, ``GET /api/v1/array/full/{path}``,
and ``GET /api/v1/table/partition/{path}`` provide options for slicing and sub-selection
specific to arrays and table. Generic clients, like a web browser,
should use the "full" routes, which send the entire (sliced) result in one
response. More sophisticated clients with some knowledge of Tiled may use the
other routes, which enable parallel chunk-based access.
a given node, with optional filtering (search). The responses contain links to
the data, in various forms.

For example, data access routes ``GET /api/v1/array/block/{path}``,
``GET /api/v1/array/full/{path}``, and ``GET /api/v1/table/partition/{path}``
provide options for slicing and sub-selection specific to arrays and table.
danielballan marked this conversation as resolved.
Show resolved Hide resolved
Generic clients, like a web browser, should use the "full" routes, which send
the entire (sliced) result in one response. More sophisticated clients with
some knowledge of Tiled may use the other routes, which enable parallel
chunk-based access.
danielballan marked this conversation as resolved.
Show resolved Hide resolved

The root route, `GET /api/v1/` provides general information about the server and the formats
and authentication providers it supports.
Expand Down
4 changes: 2 additions & 2 deletions docs/source/tutorials/plotly-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ data visualization tool.
5. Use the "Import" menu to import data by URL. Enter a URL such as

```
http://localhost:8000/api/v1/node/full/short_table?format=text/csv
http://localhost:8000/api/v1/table/full/short_table?format=text/csv
```

or, to load only certain columns,

```
http://localhost:8000/api/v1/node/full/short_table?format=text/csv&field=A&field=B
http://localhost:8000/api/v1/table/full/short_table?format=text/csv&field=A&field=B
danielballan marked this conversation as resolved.
Show resolved Hide resolved
```
6 changes: 3 additions & 3 deletions tiled/client/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ def _get_partition(self, partition, columns):
params = {"partition": partition}
if columns:
# Note: The singular/plural inconsistency here is due to the fact that
# ["A", "B"] will be encoded in the URL as field=A&field=B
params["field"] = columns
# ["A", "B"] will be encoded in the URL as column=A&column=B
params["column"] = columns
content = handle_error(
self.context.http_client.get(
self.item["links"]["partition"],
Expand Down Expand Up @@ -222,7 +222,7 @@ def export(self, filepath, columns=None, *, format=None):
"""
params = {}
if columns is not None:
params["field"] = columns
params["column"] = columns
danielballan marked this conversation as resolved.
Show resolved Hide resolved
return export_util(
filepath,
format,
Expand Down
6 changes: 3 additions & 3 deletions tiled/server/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ async def construct_resource(
d["links"] = {
"self": f"{base_url}/metadata/{path_str}",
"search": f"{base_url}/search/{path_str}",
"full": f"{base_url}/node/full/{path_str}",
"full": f"{base_url}/container/full/{path_str}",
danielballan marked this conversation as resolved.
Show resolved Hide resolved
}

resource = schemas.Resource[
Expand Down Expand Up @@ -722,8 +722,8 @@ class WrongTypeForRoute(Exception):
FULL_LINKS = {
StructureFamily.array: {"full": "{base_url}/array/full/{path}"},
StructureFamily.awkward: {"full": "{base_url}/awkward/full/{path}"},
StructureFamily.container: {"full": "{base_url}/node/full/{path}"},
StructureFamily.table: {"full": "{base_url}/node/full/{path}"},
StructureFamily.container: {"full": "{base_url}/container/full/{path}"},
StructureFamily.table: {"full": "{base_url}/table/full/{path}"},
danielballan marked this conversation as resolved.
Show resolved Hide resolved
StructureFamily.sparse: {"full": "{base_url}/array/full/{path}"},
}

Expand Down
108 changes: 105 additions & 3 deletions tiled/server/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,10 +540,111 @@ async def table_partition(
raise HTTPException(status_code=406, detail=err.args[0])


@router.get(
"/table/full/{path:path}",
response_model=schemas.Response,
name="full 'container' or 'table'",
danielballan marked this conversation as resolved.
Show resolved Hide resolved
)
async def table_full(
request: Request,
entry=SecureEntry(scopes=["read:data"]),
principal: str = Depends(get_current_principal),
padraic-shafer marked this conversation as resolved.
Show resolved Hide resolved
column: Optional[List[str]] = Query(None, min_length=1),
format: Optional[str] = None,
filename: Optional[str] = None,
serialization_registry=Depends(get_serialization_registry),
settings: BaseSettings = Depends(get_settings),
):
"""
Fetch the data below the given node.
"""
danielballan marked this conversation as resolved.
Show resolved Hide resolved
try:
with record_timing(request.state.metrics, "read"):
data = await ensure_awaitable(entry.read, column)
except KeyError as err:
(key,) = err.args
raise HTTPException(status_code=400, detail=f"No such field {key}.")
if data.memory_usage().sum() > settings.response_bytesize_limit:
raise HTTPException(
status_code=400,
detail=(
f"Response would exceed {settings.response_bytesize_limit}. "
"Select a subset of the columns to "
"request a smaller chunks."
),
)
try:
with record_timing(request.state.metrics, "pack"):
return await construct_data_response(
entry.structure_family,
serialization_registry,
data,
entry.metadata(),
request,
format,
specs=getattr(entry, "specs", []),
expires=getattr(entry, "content_stale_at", None),
filename=filename,
filter_for_access=None,
)
except UnsupportedMediaTypes as err:
raise HTTPException(status_code=406, detail=err.args[0])


@router.get(
"/container/full/{path:path}",
response_model=schemas.Response,
name="full 'container' or 'table'",
danielballan marked this conversation as resolved.
Show resolved Hide resolved
)
async def container_full(
request: Request,
entry=SecureEntry(scopes=["read:data"]),
principal: str = Depends(get_current_principal),
field: Optional[List[str]] = Query(None, min_length=1),
format: Optional[str] = None,
filename: Optional[str] = None,
serialization_registry=Depends(get_serialization_registry),
settings: BaseSettings = Depends(get_settings),
danielballan marked this conversation as resolved.
Show resolved Hide resolved
):
"""
Fetch the data below the given node.
"""
danielballan marked this conversation as resolved.
Show resolved Hide resolved
try:
with record_timing(request.state.metrics, "read"):
data = await ensure_awaitable(entry.read, field)
except KeyError as err:
(key,) = err.args
raise HTTPException(status_code=400, detail=f"No such field {key}.")
curried_filter = partial(
filter_for_access,
principal=principal,
scopes=["read:data"],
metrics=request.state.metrics,
)
# TODO Walk node to determine size before handing off to serializer.
try:
with record_timing(request.state.metrics, "pack"):
return await construct_data_response(
entry.structure_family,
serialization_registry,
data,
entry.metadata(),
request,
format,
specs=getattr(entry, "specs", []),
expires=getattr(entry, "content_stale_at", None),
filename=filename,
filter_for_access=curried_filter,
)
except UnsupportedMediaTypes as err:
raise HTTPException(status_code=406, detail=err.args[0])


@router.get(
"/node/full/{path:path}",
response_model=schemas.Response,
name="full 'container' or 'table'",
deprecated=True,
danielballan marked this conversation as resolved.
Show resolved Hide resolved
)
async def node_full(
request: Request,
Expand Down Expand Up @@ -856,9 +957,9 @@ async def post_metadata(
links[
"partition"
] = f"{base_url}/table/partition/{path_str}?partition={{index}}"
links["full"] = f"{base_url}/node/full/{path_str}"
links["full"] = f"{base_url}/table/full/{path_str}"
elif body.structure_family == StructureFamily.container:
links["full"] = f"{base_url}/node/full/{path_str}"
links["full"] = f"{base_url}/container/full/{path_str}"
danielballan marked this conversation as resolved.
Show resolved Hide resolved
links["search"] = f"{base_url}/search/{path_str}"
elif body.structure_family == StructureFamily.awkward:
links["buffers"] = f"{base_url}/awkward/buffers/{path_str}"
Expand Down Expand Up @@ -946,7 +1047,8 @@ async def put_array_block(
return json_or_msgpack(request, None)


@router.put("/node/full/{path:path}")
@router.put("/table/full/{path:path}")
@router.put("/node/full/{path:path}", deprecated=True)
danielballan marked this conversation as resolved.
Show resolved Hide resolved
async def put_node_full(
request: Request,
entry=SecureEntry(scopes=["write:data"]),
Expand Down