def test_url_pattern_works(self): re_pattern = url_pattern('/open/{{id1}}ws/{{id2}}wf') matcher = re.fullmatch(re_pattern, '/open/34ws/a66wf') self.assertIsNotNone(matcher) self.assertEqual(matcher.groupdict(), {'id1': '34', 'id2': 'a66'}) re_pattern = url_pattern('/open/ws{{id1}}/wf{{id2}}') matcher = re.fullmatch(re_pattern, '/open/ws34/wfa66') self.assertIsNotNone(matcher) self.assertEqual(matcher.groupdict(), {'id1': '34', 'id2': 'a66'}) re_pattern = url_pattern('/datasets/{{ds_id}}/data.zarr/(?P<path>.*)') matcher = re.fullmatch(re_pattern, '/datasets/S2PLUS_2017/data.zarr/') self.assertIsNotNone(matcher) self.assertEqual(matcher.groupdict(), {'ds_id': 'S2PLUS_2017', 'path': ''}) matcher = re.fullmatch(re_pattern, '/datasets/S2PLUS_2017/data.zarr/conc_chl/.zattrs') self.assertIsNotNone(matcher) self.assertEqual(matcher.groupdict(), {'ds_id': 'S2PLUS_2017', 'path': 'conc_chl/.zattrs'}) x = 'C%3A%5CUsers%5CNorman%5CIdeaProjects%5Cccitools%5Cect-core%5Ctest%5Cui%5CTEST_WS_3' re_pattern = url_pattern('/ws/{{base_dir}}/res/{{res_name}}/add') matcher = re.fullmatch(re_pattern, '/ws/%s/res/SST/add' % x) self.assertIsNotNone(matcher) self.assertEqual(matcher.groupdict(), {'base_dir': x, 'res_name': 'SST'})
def test_url_pattern_fail(self): with self.assertRaises(ValueError) as cm: url_pattern('/open/{{ws/name}}') self.assertEqual(str(cm.exception), 'name in {{name}} must be a valid identifier, but got "ws/name"') with self.assertRaises(ValueError) as cm: url_pattern('/info/{{id}') self.assertEqual(str(cm.exception), 'no matching "}}" after "{{" in "/info/{{id}"')
def test_url_pattern_ok(self): self.assertEqual('/version', url_pattern('/version')) self.assertEqual('(?P<num>[^\;\/\?\:\@\&\=\+\$\,]+)/get', url_pattern('{{num}}/get')) self.assertEqual('/open/(?P<ws_name>[^\;\/\?\:\@\&\=\+\$\,]+)', url_pattern('/open/{{ws_name}}')) self.assertEqual('/open/ws(?P<id1>[^\;\/\?\:\@\&\=\+\$\,]+)/wf(?P<id2>[^\;\/\?\:\@\&\=\+\$\,]+)', url_pattern('/open/ws{{id1}}/wf{{id2}}')) self.assertEqual('/datasets/(?P<ds_id>[^\;\/\?\:\@\&\=\+\$\,]+)/data.zip/(.*)', url_pattern('/datasets/{{ds_id}}/data.zip/(.*)'))
def new_application(prefix: str = None): prefix = normalize_prefix(prefix) application = Application([ (prefix + '/res/(.*)', StaticFileHandler, { 'path': os.path.join(os.path.dirname(__file__), 'res') }), (prefix + url_pattern('/'), InfoHandler), (prefix + url_pattern('/wmts/1.0.0/WMTSCapabilities.xml'), GetWMTSCapabilitiesXmlHandler), (prefix + url_pattern( '/wmts/1.0.0/tile/{{ds_id}}/{{var_name}}/{{z}}/{{y}}/{{x}}.png'), GetWMTSTileHandler), (prefix + url_pattern('/wmts/kvp'), WMTSKvpHandler), (prefix + url_pattern('/datasets'), GetDatasetsHandler), (prefix + url_pattern('/datasets/{{ds_id}}'), GetDatasetHandler), (prefix + url_pattern('/datasets/{{ds_id}}/places'), GetDatasetPlaceGroupsHandler), (prefix + url_pattern('/datasets/{{ds_id}}/places/{{place_group_id}}'), GetDatasetPlaceGroupHandler), (prefix + url_pattern('/datasets/{{ds_id}}/coords/{{dim_name}}'), GetDatasetCoordsHandler), (prefix + url_pattern('/datasets/{{ds_id}}/vars/{{var_name}}/legend.png'), GetDatasetVarLegendHandler), (prefix + url_pattern( '/datasets/{{ds_id}}/vars/{{var_name}}/tiles/{{z}}/{{x}}/{{y}}.png' ), GetDatasetVarTileHandler), (prefix + url_pattern('/datasets/{{ds_id}}/vars/{{var_name}}/tilegrid'), GetDatasetVarTileGridHandler), # AWS S3 compatible data access as ZARR (prefix + url_pattern('/s3bucket/{{ds_id}}/(?P<path>.*)'), GetS3BucketObjectHandler), (prefix + url_pattern('/s3bucket/{{ds_id}}'), GetS3BucketObjectHandler), (prefix + url_pattern('/s3bucket'), ListS3BucketHandler), # Natural Earth 2 tiles for testing (prefix + url_pattern('/ne2/tilegrid'), GetNE2TileGridHandler), (prefix + url_pattern('/ne2/tiles/{{z}}/{{x}}/{{y}}.jpg'), GetNE2TileHandler), # Color Bars API (prefix + url_pattern('/colorbars'), GetColorBarsJsonHandler), (prefix + url_pattern('/colorbars.html'), GetColorBarsHtmlHandler), # Places API (PRELIMINARY & UNSTABLE - will be revised soon) (prefix + url_pattern('/places'), GetPlaceGroupsHandler), (prefix + url_pattern('/places/{{place_group_id}}'), FindPlacesHandler), (prefix + url_pattern('/places/{{place_group_id}}/{{ds_id}}'), FindDatasetPlacesHandler), # Time-series API (for VITO's DCS4COP viewer only, PRELIMINARY & UNSTABLE - will be revised soon) (prefix + url_pattern('/ts'), GetTimeSeriesInfoHandler), (prefix + url_pattern('/ts/{{ds_id}}/{{var_name}}/point'), GetTimeSeriesForPointHandler), (prefix + url_pattern('/ts/{{ds_id}}/{{var_name}}/geometry'), GetTimeSeriesForGeometryHandler), (prefix + url_pattern('/ts/{{ds_id}}/{{var_name}}/geometries'), GetTimeSeriesForGeometriesHandler), (prefix + url_pattern('/ts/{{ds_id}}/{{var_name}}/features'), GetTimeSeriesForFeaturesHandler), ]) return application
def new_application(route_prefix: str = None, base_dir: str = '.'): route_prefix = normalize_prefix(route_prefix) application = Application([ (route_prefix + '/res/(.*)', StaticFileHandler, { 'path': os.path.join(os.path.dirname(__file__), 'res') }), (route_prefix + '/images/(.*)', StaticFileHandler, { 'path': os.path.join(base_dir, 'images') }), (route_prefix + url_pattern('/'), xcube.webapi.handlers.InfoHandler), (route_prefix + url_pattern('/wmts/1.0.0/WMTSCapabilities.xml'), xcube.webapi.handlers.GetWMTSCapabilitiesXmlHandler), (route_prefix + url_pattern( '/wmts/1.0.0/tile/{{ds_id}}/{{var_name}}/{{z}}/{{y}}/{{x}}.png'), xcube.webapi.handlers.GetWMTSTileHandler), (route_prefix + url_pattern('/wmts/kvp'), xcube.webapi.handlers.WMTSKvpHandler), (route_prefix + url_pattern('/datasets'), xcube.webapi.handlers.GetDatasetsHandler), (route_prefix + url_pattern('/datasets/{{ds_id}}'), xcube.webapi.handlers.GetDatasetHandler), (route_prefix + url_pattern('/datasets/{{ds_id}}/places'), xcube.webapi.handlers.GetDatasetPlaceGroupsHandler), (route_prefix + url_pattern('/datasets/{{ds_id}}/places/{{place_group_id}}'), xcube.webapi.handlers.GetDatasetPlaceGroupHandler), (route_prefix + url_pattern('/datasets/{{ds_id}}/coords/{{dim_name}}'), xcube.webapi.handlers.GetDatasetCoordsHandler), (route_prefix + url_pattern('/datasets/{{ds_id}}/vars/{{var_name}}/legend.png'), xcube.webapi.handlers.GetDatasetVarLegendHandler), (route_prefix + url_pattern( '/datasets/{{ds_id}}/vars/{{var_name}}/tiles/{{z}}/{{x}}/{{y}}.png' ), xcube.webapi.handlers.GetDatasetVarTileHandler), (route_prefix + url_pattern('/datasets/{{ds_id}}/vars/{{var_name}}/tilegrid'), xcube.webapi.handlers.GetDatasetVarTileGridHandler), # AWS S3 compatible data access as ZARR (route_prefix + url_pattern('/s3bucket/{{ds_id}}/(?P<path>.*)'), xcube.webapi.handlers.GetS3BucketObjectHandler), (route_prefix + url_pattern('/s3bucket/{{ds_id}}'), xcube.webapi.handlers.GetS3BucketObjectHandler), (route_prefix + url_pattern('/s3bucket'), xcube.webapi.handlers.ListS3BucketHandler), # Color Bars API (route_prefix + url_pattern('/colorbars'), xcube.webapi.handlers.GetColorBarsJsonHandler), (route_prefix + url_pattern('/colorbars.html'), xcube.webapi.handlers.GetColorBarsHtmlHandler), # Places API (PRELIMINARY & UNSTABLE - will be revised soon) (route_prefix + url_pattern('/places'), xcube.webapi.handlers.GetPlaceGroupsHandler), (route_prefix + url_pattern('/places/{{place_group_id}}'), xcube.webapi.handlers.FindPlacesHandler), (route_prefix + url_pattern('/places/{{place_group_id}}/{{ds_id}}'), xcube.webapi.handlers.FindDatasetPlacesHandler), # Time-Series API (route_prefix + url_pattern('/timeseries/{{ds_id}}/{{var_name}}'), xcube.webapi.handlers.GetTimeSeriesHandler), # Legacy time-series API (for VITO's DCS4COP viewer only) (route_prefix + url_pattern('/ts'), xcube.webapi.handlers.GetTsLegacyInfoHandler), (route_prefix + url_pattern('/ts/{{ds_id}}/{{var_name}}/point'), xcube.webapi.handlers.GetTsLegacyForPointHandler), (route_prefix + url_pattern('/ts/{{ds_id}}/{{var_name}}/geometry'), xcube.webapi.handlers.GetTsLegacyForGeometryHandler), (route_prefix + url_pattern('/ts/{{ds_id}}/{{var_name}}/geometries'), xcube.webapi.handlers.GetTsLegacyForGeometriesHandler), (route_prefix + url_pattern('/ts/{{ds_id}}/{{var_name}}/features'), xcube.webapi.handlers.GetTsLegacyForFeaturesHandler), ]) return application