Compare commits

...
12 Commits
Author SHA1 Message Date
Alok Saldanha 9c38e48c5c prepare for 0.3.8 release 2021-12-21 12:19:02 -05:00
Alok Saldanha 073f5f945c #57 changed logic to take last path element 2021-12-21 12:05:01 -05:00
Alok Saldanha 9dc4409f1a #57 added failing unit test 2021-12-21 12:03:39 -05:00
Alok Saldanha 551cb46af8 #42 add support for is_authorized hook 2021-11-14 17:28:10 -05:00
Alok Saldanha 620181ae4d prepare for 0.3.7 release 2021-08-12 14:02:26 -04:00
Alok Saldanha 73a7920cc8 add back ip_address endpoint 2021-08-12 13:58:19 -04:00
Alok Saldanha ed3e999cd1 prepare for 0.3.6 release 2021-07-18 10:42:42 -04:00
Alok Saldanha 2ae2e53863 Pin version of workzeug
This is required by earlier flask-api versions

  File "/home/alokito/code/cellxgene-gateway/cellxgene_gateway/gateway.py", line 25, in <module>
    from flask_api import status
  File "/home/alokito/miniconda3/envs/cellxgene-gateway/lib/python3.7/site-packages/flask_api/__init__.py", line 1, in <module>
    from flask_api.app import FlaskAPI
  File "/home/alokito/miniconda3/envs/cellxgene-gateway/lib/python3.7/site-packages/flask_api/app.py", line 6, in <module>
    from flask_api.request import APIRequest
  File "/home/alokito/miniconda3/envs/cellxgene-gateway/lib/python3.7/site-packages/flask_api/request.py", line 9, in <module>
    from werkzeug._compat import to_unicode
ModuleNotFoundError: No module named 'werkzeug._compat'
2021-07-18 10:32:10 -04:00
Alok Saldanha fd0e7d9c31 preparing for 0.3.5 release 2021-07-18 09:43:45 -04:00
Alok Saldanha 98ef6efd0c pinned version of flask, to match cellxgene 2021-07-18 09:39:52 -04:00
Alok Saldanha 82e43ff943 preparing for 0.3.4 release 2021-07-18 09:15:27 -04:00
Alok Saldanha f8a77423eb Merge pull request #51 from Novartis/nested_subdirs
Enable listing nested subdirs
2021-07-18 09:14:16 -04:00
13 changed files with 81 additions and 15 deletions
-1
View File
@@ -8,7 +8,6 @@ repos:
types: [python] types: [python]
stages: [commit] stages: [commit]
- id: black - id: black
language_version: python3.6+
name: black name: black
language: system language: system
entry: black entry: black
+20
View File
@@ -1,3 +1,23 @@
# 0.3.8
* Fixed bug #57 affecting deeply nested subdirectory listing
# 0.3.7
* added back /metadata/ip_address endpoint
# 0.3.6
* pinned version of werkzeug
# 0.3.5
* Pinned flask version to match cellxgene 0.17.0
# 0.3.4
* Fixed bug #50 affecting subdirectory listing
# 0.3.3 # 0.3.3
* Fixed bug #48 affecting cache pruning * Fixed bug #48 affecting cache pruning
+1 -1
View File
@@ -7,4 +7,4 @@
# OR CONDITIONS OF ANY KIND, either express or implied. See the License for # OR CONDITIONS OF ANY KIND, either express or implied. See the License for
# the specific language governing permissions and limitations under the License. # the specific language governing permissions and limitations under the License.
__version__ = "0.3.3" __version__ = "0.3.8"
+1 -1
View File
@@ -54,7 +54,7 @@ def render_item_tree(item_tree, item_source):
if item_tree.descriptor: if item_tree.descriptor:
descriptor = item_tree.descriptor.lstrip("/") descriptor = item_tree.descriptor.lstrip("/")
url = f"/filecrawl/{descriptor}?source={item_source.name}" url = f"/filecrawl/{descriptor}?source={item_source.name}"
name = descriptor.rsplit("/")[1] if descriptor.find("/") >= 0 else descriptor name = descriptor.rsplit("/", 1)[-1]
return f"<li><a href='{url}'>{name}</a>{html}</li>" return f"<li><a href='{url}'>{name}</a>{html}</li>"
else: else:
return html return html
+19 -5
View File
@@ -52,6 +52,14 @@ def _force_https(app):
return wrapper return wrapper
def set_no_cache(resp):
resp.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
resp.headers["Pragma"] = "no-cache"
resp.headers["Expires"] = "0"
resp.headers["Cache-Control"] = "public, max-age=0"
return resp
app.wsgi_app = _force_https(app.wsgi_app) app.wsgi_app = _force_https(app.wsgi_app)
if ( if (
env.proxy_fix_for > 0 env.proxy_fix_for > 0
@@ -157,10 +165,7 @@ def filecrawl(path=None):
path=path, path=path,
) )
) )
resp.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" set_no_cache(resp)
resp.headers["Pragma"] = "no-cache"
resp.headers["Expires"] = "0"
resp.headers["Cache-Control"] = "public, max-age=0"
return resp return resp
@@ -209,7 +214,10 @@ def do_view(path, source_name=None):
match.status == CacheEntryStatus.loaded match.status == CacheEntryStatus.loaded
or match.status == CacheEntryStatus.loading or match.status == CacheEntryStatus.loading
): ):
return match.serve_content(path) if source.is_authorized(match.key.descriptor):
return match.serve_content(path)
else:
raise CellxgeneException("User not authorized to access this data", 403)
elif match.status == CacheEntryStatus.error: elif match.status == CacheEntryStatus.error:
raise ProcessException.from_cache_entry(match) raise ProcessException.from_cache_entry(match)
@@ -267,6 +275,12 @@ def do_terminate(path):
return redirect(url_for("do_GET_status"), code=302) return redirect(url_for("do_GET_status"), code=302)
@app.route("/metadata/ip_address", methods=["GET"])
def ip_address():
resp = make_response(env.ip)
return set_no_cache(resp)
def launch(): def launch():
env.validate() env.validate()
if not item_sources or not len(item_sources): if not item_sources or not len(item_sources):
@@ -121,6 +121,9 @@ class FileItemSource(ItemSource):
if self.is_h5ad_file(full_path): if self.is_h5ad_file(full_path):
return self.shallowitem_from_descriptor(descriptor) return self.shallowitem_from_descriptor(descriptor)
def is_authorized(self, descriptor):
return True
def lookup(self, indescriptor: str) -> LookupResult: def lookup(self, indescriptor: str) -> LookupResult:
descriptor = indescriptor.strip("/") descriptor = indescriptor.strip("/")
if descriptor.endswith(self.annotation_file_suffix): if descriptor.endswith(self.annotation_file_suffix):
+4
View File
@@ -40,6 +40,10 @@ class ItemSource(ABC):
def update(self, item: Item) -> None: def update(self, item: Item) -> None:
raise Exception('"update" unimplemented') raise Exception('"update" unimplemented')
@abstractmethod
def is_authorized(self, descriptor: str) -> bool:
raise Exception('"is_authorized" unimplemented')
@abstractmethod @abstractmethod
def lookup(self, descriptor: str) -> LookupResult: def lookup(self, descriptor: str) -> LookupResult:
raise Exception('"lookup" unimplemented') raise Exception('"lookup" unimplemented')
@@ -113,6 +113,9 @@ class S3ItemSource(ItemSource):
def update(self, item: S3Item) -> None: def update(self, item: S3Item) -> None:
pass pass
def is_authorized(self, descriptor):
return True
def lookup_item(self, descriptor): def lookup_item(self, descriptor):
full_path = self.url(descriptor) full_path = self.url(descriptor)
if self.is_h5ad_url(full_path): if self.is_h5ad_url(full_path):
@@ -75,7 +75,9 @@
const el = $(this); const el = $(this);
const ts = el.text(); const ts = el.text();
const dt = new Date(parseInt(ts * 1000)); const dt = new Date(parseInt(ts * 1000));
el.html(`${dt.toISOString()}<br>(${ts})`); el.prepend(`${dt.toISOString()}<br>(`);
el.append(')');
}); });
}) })
</script> </script>
+6 -2
View File
@@ -4,11 +4,15 @@ channels:
dependencies: dependencies:
- python=3.7 - python=3.7
- requests - requests
- flask - flask<2.0.0,>=1.0.2
- psutil - psutil
- black - black
- twine
- isort
- coverage - coverage
- pip - pip
- pip: - pip:
- flask-api - pre_commit
- flask-api==2.0
- werkzeug==1.0.1
- cellxgene>=0.15 - cellxgene>=0.15
+3 -2
View File
@@ -1,5 +1,6 @@
cellxgene>=0.15 cellxgene>=0.15
flask flask<2.0.0,>=1.0.2
flask_api flask-api==2.0
werkzeug==1.0.1
psutil psutil
requests requests
+17 -1
View File
@@ -1,7 +1,11 @@
import unittest import unittest
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from cellxgene_gateway.filecrawl import render_item, render_item_source from cellxgene_gateway.filecrawl import (
render_item,
render_item_source,
render_item_tree,
)
from cellxgene_gateway.items.file.fileitem import FileItem from cellxgene_gateway.items.file.fileitem import FileItem
from cellxgene_gateway.items.file.fileitem_source import FileItemSource from cellxgene_gateway.items.file.fileitem_source import FileItemSource
from cellxgene_gateway.items.item import ItemTree, ItemType from cellxgene_gateway.items.item import ItemTree, ItemType
@@ -41,3 +45,15 @@ class TestRenderItemSource(unittest.TestCase):
rendered, rendered,
"<h6><a href='/filecrawl.html?source=FakeSource'>FakeSource</a>:some_filter</h6><li><a href='/filecrawl/rootdir?source=FakeSource'>rootdir</a><ul></ul></li>", "<h6><a href='/filecrawl.html?source=FakeSource'>FakeSource</a>:some_filter</h6><li><a href='/filecrawl/rootdir?source=FakeSource'>rootdir</a><ul></ul></li>",
) )
class TestRenderItemTree(unittest.TestCase):
@patch("cellxgene_gateway.items.file.fileitem_source.FileItemSource")
def test_GIVEN_deep_nested_dirs_THEN_includes_dirs_in_output(self, item_source):
item_source.name = "FakeSource"
item_tree = ItemTree("foo/bar/baz", [], [])
rendered = render_item_tree(item_tree, item_source)
self.assertEqual(
rendered,
"<li><a href='/filecrawl/foo/bar/baz?source=FakeSource'>baz</a><ul></ul></li>",
)
+1 -1
View File
@@ -33,7 +33,7 @@ class TestSubprocessBackend(unittest.TestCase):
backend.launch(cellxgene_loc, scripts, entry) backend.launch(cellxgene_loc, scripts, entry)
popen.assert_called_once_with( popen.assert_called_once_with(
[ [
"yes | /some/cellxgene launch /tmp/czi/pbmc3k.h5ad --port 8000 --host 127.0.0.1 --disable-annotations --scripts http://example.com/script.js --scripts http://example.com/script2.js" "yes | /some/cellxgene launch /tmp/czi/pbmc3k.h5ad --port 8000 --host 127.0.0.1 --disable-annotations --disable-gene-sets-save --scripts http://example.com/script.js --scripts http://example.com/script2.js"
], ],
shell=True, shell=True,
stderr=-1, stderr=-1,