import type { ArrayShape , Attribute , AttributeValues , Dataset , Entity , Group , GroupWithChildren , NumericType , UnresolvedEntity , } from '@h5web/shared' ; import { hasScalarShape , buildEntityPath , EntityKind } from '@h5web/shared' ; import { isString } from 'lodash' ; import { ProviderApi } from '../api' ; import type { ExportFormat , ValuesStoreParams } from '../models' ; import { convertDtype , handleAxiosError } from '../utils' ; import type { H5GroveAttribute , H5GroveAttrValuesResponse , H5GroveDataResponse , H5GroveEntityResponse , } from './models' ; import { isDatasetResponse , isExternalLinkResponse , isGroupResponse , isSoftLinkResponse , typedArrayFromDType , } from './utils' ; export class H5GroveApi extends ProviderApi { public constructor ( url , filepath , axiosParams ? ) { super ( filepath , { baseURL : url , params : axiosParams } ) ; } public async getEntity ( path ) { const response = await this . fetchEntity ( path ) ; return this . processEntityResponse ( path , response ) ; } public async getValue ( params ) { const { dataset } = params ; const DTypedArray = typedArrayFromDType ( dataset . type ) ; if ( DTypedArray ) { const buffer = await this . fetchBinaryData ( params ) ; const array = new DTypedArray ( buffer ) ; return hasScalarShape ( dataset ) ? array [ 0 ] : [ ... array ] ; } return this . fetchData ( params ) ; } public async getAttrValues ( entity ) { const { path , attributes } = entity ; return attributes . length > 0 ? this . fetchAttrValues ( path ) : { } ; } public getExportURL ( dataset , selection , format ) { const { baseURL , params } = this . client . defaults ; const searchParams = new URLSearchParams ( params as Record < string , string > ) ; searchParams . set ( 'path' , dataset . path ) ; searchParams . set ( 'format' , format ) ; if ( selection ) { searchParams . set ( 'selection' , selection ) ; } return `${ baseURL as string }/data/?${ searchParams . toString ( ) }` ; } private async fetchEntity ( path ) { const { data } = await handleAxiosError ( ( ) => this . client . get < H5GroveEntityResponse > ( `/meta/` , { params : { path } } ) , ( status , errorData ) => { if ( status !== 404 || ! isString ( errorData ) ) { return undefined ; } if ( errorData . includes ( 'File not found' ) ) { return `File not found: '${ this . filepath }'` ; } if ( errorData . includes ( 'not a valid path' ) ) { return `No entity found at ${ path }` ; } if ( errorData . includes ( 'Cannot resolve' ) ) { return `Could not resolve soft link at ${ path }` ; } return undefined ; } ) ; return data ; } private async fetchAttrValues ( path ) { const { data } = await this . client . get < H5GroveAttrValuesResponse > ( `/attr/` , { params : { path } } ) ; return data ; } private async fetchData ( params ) { const { data } = await this . cancellableFetchValue < H5GroveDataResponse > ( `/data/` , params , { path : params . dataset . path , selection : params . selection , flatten : true } ) ; return data ; } private async fetchBinaryData ( params ) { const { data } = await this . cancellableFetchValue < ArrayBuffer > ( '/data/' , params , { path : params . dataset . path , selection : params . selection , format : 'bin' } , 'arraybuffer' ) ; return data ; } private async processEntityResponse ( path , response ) { const { name , type : kind } = response ; const baseEntity = { name , path , kind } ; if ( isGroupResponse ( response ) ) { const { children , attributes : attrsMetadata } = response ; const attributes = await this . processAttrsMetadata ( path , attrsMetadata ) ; if ( ! children ) { return { ... baseEntity , attributes } as Group ; } return { ... baseEntity , attributes , children : await Promise . all ( children . map ( ( child ) => this . processEntityResponse ( buildEntityPath ( path , child . name ) , child ) ) ) , } as GroupWithChildren ; } if ( isDatasetResponse ( response ) ) { const { attributes : attrsMetadata , dtype , shape } = response ; const attributes = await this . processAttrsMetadata ( path , attrsMetadata ) ; return { ... baseEntity , attributes , shape , type : convertDtype ( dtype ) , rawType : dtype , } as Dataset ; } if ( isSoftLinkResponse ( response ) ) { const { target_path } = response ; return { ... baseEntity , attributes : [ ] , kind : EntityKind . Unresolved , link : { class : 'Soft' , path : target_path } , } ; } if ( isExternalLinkResponse ( response ) ) { const { target_file , target_path } = response ; return { ... baseEntity , kind : EntityKind . Unresolved , attributes : [ ] , link : { class : 'External' , file : target_file , path : target_path , } , } ; } return { ... baseEntity , kind : EntityKind . Unresolved , } as UnresolvedEntity ; } private async processAttrsMetadata ( path , attrsMetadata ) { return attrsMetadata . map < Attribute > ( ( { name , dtype , shape } ) => ( { name , shape , type : convertDtype ( dtype ) , } ) ) ; } }