diff --git a/tiled/server/core.py b/tiled/server/core.py index 857d547a0..f3d9efcda 100644 --- a/tiled/server/core.py +++ b/tiled/server/core.py @@ -34,6 +34,7 @@ ) from . import schemas from .etag import tokenize +from .links import links_for_node from .utils import record_timing del queries @@ -404,6 +405,7 @@ async def construct_resource( depth=0, ): path_str = "/".join(path_parts) + id_ = path_parts[-1] if path_parts else "" attributes = {"ancestors": path_parts[:-1]} if include_data_sources and hasattr(entry, "data_sources"): attributes["data_sources"] = entry.data_sources @@ -488,15 +490,16 @@ async def construct_resource( for key, direction in entry.sorting ] d = { - "id": path_parts[-1] if path_parts else "", + "id": id_, "attributes": schemas.NodeAttributes(**attributes), } if not omit_links: - d["links"] = { - "self": f"{base_url}/metadata/{path_str}", - "search": f"{base_url}/search/{path_str}", - "full": f"{base_url}/container/full/{path_str}", - } + d["links"] = links_for_node( + entry.structure_family, + entry.structure(), + base_url, + path_str, + ) resource = schemas.Resource[ schemas.NodeAttributes, schemas.ContainerLinks, schemas.ContainerMeta @@ -510,34 +513,16 @@ async def construct_resource( entry.structure_family ] links.update( - { - link: template.format(base_url=base_url, path=path_str) - for link, template in FULL_LINKS[entry.structure_family].items() - } + links_for_node( + entry.structure_family, + entry.structure(), + base_url, + path_str, + ) ) structure = asdict(entry.structure()) if schemas.EntryFields.structure_family in fields: attributes["structure_family"] = entry.structure_family - if entry.structure_family == StructureFamily.sparse: - shape = structure.get("shape") - block_template = ",".join(f"{{{index}}}" for index in range(len(shape))) - links[ - "block" - ] = f"{base_url}/array/block/{path_str}?block={block_template}" - elif entry.structure_family == StructureFamily.array: - shape = structure.get("shape") - block_template = ",".join( - f"{{index_{index}}}" for index in range(len(shape)) - ) - links[ - "block" - ] = f"{base_url}/array/block/{path_str}?block={block_template}" - elif entry.structure_family == StructureFamily.table: - links[ - "partition" - ] = f"{base_url}/table/partition/{path_str}?partition={{index}}" - elif entry.structure_family == StructureFamily.awkward: - links["buffers"] = f"{base_url}/awkward/buffers/{path_str}" if schemas.EntryFields.structure in fields: attributes["structure"] = structure else: diff --git a/tiled/server/links.py b/tiled/server/links.py new file mode 100644 index 000000000..76bf2616f --- /dev/null +++ b/tiled/server/links.py @@ -0,0 +1,53 @@ +""" +Generate the 'links' section of the response JSON. + +The links vary by structure family. +""" +from ..structures.core import StructureFamily + + +def links_for_node(structure_family, structure, base_url, path_str): + links = {} + links = LINKS_BY_STRUCTURE_FAMILY[structure_family]( + structure_family, structure, base_url, path_str + ) + links["self"] = f"{base_url}/metadata/{path_str}" + return links + + +def links_for_array(structure_family, structure, base_url, path_str): + links = {} + block_template = ",".join(f"{{{index}}}" for index in range(len(structure.shape))) + links["block"] = f"{base_url}/array/block/{path_str}?block={block_template}" + links["full"] = f"{base_url}/array/full/{path_str}" + return links + + +def links_for_awkward(structure_family, structure, base_url, path_str): + links = {} + links["buffers"] = f"{base_url}/awkward/buffers/{path_str}" + links["full"] = f"{base_url}/awkward/full/{path_str}" + return links + + +def links_for_container(structure_family, structure, base_url, path_str): + links = {} + links["full"] = f"{base_url}/container/full/{path_str}" + links["search"] = f"{base_url}/search/{path_str}" + return links + + +def links_for_table(structure_family, structure, base_url, path_str): + links = {} + links["partition"] = f"{base_url}/table/partition/{path_str}?partition={{index}}" + links["full"] = f"{base_url}/table/full/{path_str}" + return links + + +LINKS_BY_STRUCTURE_FAMILY = { + StructureFamily.array: links_for_array, + StructureFamily.awkward: links_for_awkward, + StructureFamily.container: links_for_container, + StructureFamily.sparse: links_for_array, # spare and array are the same + StructureFamily.table: links_for_table, +} diff --git a/tiled/server/router.py b/tiled/server/router.py index 9d7db130b..62612e884 100644 --- a/tiled/server/router.py +++ b/tiled/server/router.py @@ -44,6 +44,7 @@ get_validation_registry, slice_, ) +from .links import links_for_node from .settings import get_settings from .utils import filter_for_access, get_base_url, record_timing @@ -1172,30 +1173,9 @@ async def _create_node( specs=body.specs, data_sources=body.data_sources, ) - links = {} - base_url = get_base_url(request) - path_parts = [segment for segment in path.split("/") if segment] + [key] - path_str = "/".join(path_parts) - links["self"] = f"{base_url}/metadata/{path_str}" - if body.structure_family in {StructureFamily.array, StructureFamily.sparse}: - block_template = ",".join( - f"{{{index}}}" for index in range(len(node.structure().shape)) - ) - links["block"] = f"{base_url}/array/block/{path_str}?block={block_template}" - links["full"] = f"{base_url}/array/full/{path_str}" - elif body.structure_family == StructureFamily.table: - links[ - "partition" - ] = f"{base_url}/table/partition/{path_str}?partition={{index}}" - links["full"] = f"{base_url}/table/full/{path_str}" - elif body.structure_family == StructureFamily.container: - links["full"] = f"{base_url}/container/full/{path_str}" - links["search"] = f"{base_url}/search/{path_str}" - elif body.structure_family == StructureFamily.awkward: - links["buffers"] = f"{base_url}/awkward/buffers/{path_str}" - links["full"] = f"{base_url}/awkward/full/{path_str}" - else: - raise NotImplementedError(body.structure_family) + links = links_for_node( + structure_family, structure, get_base_url(request), path + f"/{key}" + ) response_data = { "id": key, "links": links, diff --git a/tiled/server/schemas.py b/tiled/server/schemas.py index 94c893aa1..8080428cd 100644 --- a/tiled/server/schemas.py +++ b/tiled/server/schemas.py @@ -137,9 +137,9 @@ class DataSource(pydantic.BaseModel): Union[ ArrayStructure, AwkwardStructure, - TableStructure, NodeStructure, SparseStructure, + TableStructure, ] ] = None mimetype: Optional[str] = None @@ -169,9 +169,9 @@ class NodeAttributes(pydantic.BaseModel): Union[ ArrayStructure, AwkwardStructure, - TableStructure, NodeStructure, SparseStructure, + TableStructure, ] ] sorting: Optional[List[SortingItem]] @@ -218,11 +218,11 @@ class SparseLinks(pydantic.BaseModel): resource_links_type_by_structure_family = { - StructureFamily.container: ContainerLinks, StructureFamily.array: ArrayLinks, StructureFamily.awkward: AwkwardLinks, - StructureFamily.table: DataFrameLinks, + StructureFamily.container: ContainerLinks, StructureFamily.sparse: SparseLinks, + StructureFamily.table: DataFrameLinks, }