From 0f83a11644ac96fdcaec123c6d4af7b4d025c793 Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Fri, 25 Aug 2023 12:09:05 -0400 Subject: [PATCH 01/18] selective access types and defaulting --- CHANGELOG.rst | 5 +++++ pyproject.toml | 2 +- snovault/drs.py | 15 +++++++++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d6837d1c8..9aa7dfa2d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,11 @@ snovault Change Log ---------- +10.0.3 +====== + +* Update ``drs`` primitive to resolve specific access types with preferential defaulting to https, http + 10.0.2 ====== diff --git a/pyproject.toml b/pyproject.toml index a90fb760a..40b3d9634 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dcicsnovault" -version = "10.0.2" +version = "10.0.3" description = "Storage support for 4DN Data Portals." authors = ["4DN-DCIC Team "] license = "MIT" diff --git a/snovault/drs.py b/snovault/drs.py index 028385ac6..0239f7760 100644 --- a/snovault/drs.py +++ b/snovault/drs.py @@ -63,10 +63,21 @@ def get_drs_url(request, object_uri): """ Does 2 calls - one to verify the object_uri is in fact a valid DRS object and another to get the bytes of the DRS object """ try: + default_method = None + requested_download_method = request.path.split('/')[-1] drs_obj = get_and_format_drs_object(request, object_uri) access_methods = drs_obj.get('access_methods', []) - # in our system there is only 1 access method - HTTPS to S3 - return access_methods[0]['access_url'] + for access_method in access_methods: + if access_method['type'] == 'https': # prefer https + default_method = access_method['access_url'] + if not default_method and access_method['type'] == 'http': # allow http + default_method = access_method['access_url'] + if access_method['type'] == requested_download_method: + return access_method['access_url'] + else: + if default_method: + return default_method + raise Exception # no https default results in exception except Exception as e: raise HTTPNotFound(f'You accessed a DRS object that either you do not have access to,' f' did not pass valid access_id or does not exist {str(e)}') From a7bcaae7825ebbb5247eb2ec766918cdd24ca604 Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Mon, 28 Aug 2023 15:10:02 -0400 Subject: [PATCH 02/18] small changes, take schema filename --- snovault/resources.py | 7 +++++-- snovault/schema_utils.py | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/snovault/resources.py b/snovault/resources.py index ec10da13c..a35860e76 100644 --- a/snovault/resources.py +++ b/snovault/resources.py @@ -422,8 +422,11 @@ def __name__(self): present, otherwise `self.uuid` """ if self.name_key is None: - return str(self.uuid) - return self.properties.get(self.name_key, None) or str(self.uuid) + return self.uuid + properties = self.upgrade_properties() + if properties.get('status') == 'replaced': + return self.uuid + return properties.get(self.name_key, None) or self.uuid @property def properties(self): diff --git a/snovault/schema_utils.py b/snovault/schema_utils.py index cb799c712..dec0abc41 100644 --- a/snovault/schema_utils.py +++ b/snovault/schema_utils.py @@ -355,11 +355,12 @@ class SchemaValidator(SerializingSchemaValidator): def load_schema(filename): - filename = favor_app_specific_schema(filename) if isinstance(filename, dict): schema = filename resolver = NoRemoteResolver.from_schema(schema) else: + if ':' not in filename: # no repo in filename path, favor the app then fallthrough + filename = favor_app_specific_schema(filename) utf8 = codecs.getreader("utf-8") asset = AssetResolver(caller_package()).resolve(filename) schema = json.load(utf8(asset.stream()), From 67f883cc869d1a81d768267d7373ebf707537a6e Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Thu, 6 Jun 2024 13:39:37 -0400 Subject: [PATCH 03/18] add fix for drs json renderer --- snovault/renderers.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/snovault/renderers.py b/snovault/renderers.py index 0449e39b3..047a6e02f 100644 --- a/snovault/renderers.py +++ b/snovault/renderers.py @@ -361,6 +361,12 @@ def best_mime_type(request, mode=MIME_TYPE_TRIAGE_MODE): else: mime_type, score = options[0] result = mime_type + + # Force JSON for any DRS APIs + # This is safe since all portal support JSON/DRS + if '/ga4gh/drs/' in request.url or '@@drs' in request.url: + result = MIME_TYPE_JSON + if DEBUG_MIME_TYPES: PRINT("Using mime type", result, "for", request.method, request.url) for k, v in request.headers.items(): From 0d2f3cee82d51fd2c191338f0d1a56e2b34a07b4 Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Thu, 6 Jun 2024 13:40:50 -0400 Subject: [PATCH 04/18] add back cl --- CHANGELOG.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bbcb09c1b..9c4442498 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -203,6 +203,12 @@ Change Log expiration is enabled +10.0.3 +====== + +* Update ``drs`` primitive to resolve specific access types with preferential defaulting to https, http + + 10.0.2 ====== From 81824b59a8906d082f68445dfde7f27971e2ca5c Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Fri, 7 Jun 2024 10:30:30 -0400 Subject: [PATCH 05/18] add test --- snovault/tests/test_drs.py | 7 +++++++ snovault/tests/testappfixtures.py | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/snovault/tests/test_drs.py b/snovault/tests/test_drs.py index 985f4f94d..d5038969a 100644 --- a/snovault/tests/test_drs.py +++ b/snovault/tests/test_drs.py @@ -72,3 +72,10 @@ def test_drs_get_object_failure(self, testapp, testing_download): # noQA fixtur testapp.get(f'/ga4gh/drs/v1/objects/access') with pytest.raises(Exception): testapp.get(f'/ga4gh/drs/v1/objects/{drs_object_uri}/accesss/https') + + def test_drs_get_object_returns_json(self, testapp, htmltestapp, testing_download): + """ Tests that even with an htmltestapp, JSON is returned """ + res = testapp.get(testing_download) + drs_object_uri = res.json['uuid'] + resp = htmltestapp.get(f'/ga4gh/drs/v1/objects/{drs_object_uri}') + assert resp.content_type == 'application/json' diff --git a/snovault/tests/testappfixtures.py b/snovault/tests/testappfixtures.py index fd6a79cd6..aa026b18d 100644 --- a/snovault/tests/testappfixtures.py +++ b/snovault/tests/testappfixtures.py @@ -114,6 +114,17 @@ def anontestapp(app): return webtest.TestApp(app, environ) +@pytest.fixture +def htmltestapp(app): + """TestApp for TEST user, accepting text/html content.""" + environ = { + 'HTTP_ACCEPT': 'text/html', + 'REMOTE_USER': 'TEST', + } + test_app = webtest.TestApp(app, environ) + return test_app + + @pytest.fixture def authenticated_testapp(app): """ TestApp with JSON accept header for non-admin user. """ From 2d128d135828f3b5ed2995a2954f217eee7e7d18 Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Fri, 7 Jun 2024 10:34:30 -0400 Subject: [PATCH 06/18] fix static checks --- snovault/tests/test_drs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snovault/tests/test_drs.py b/snovault/tests/test_drs.py index d5038969a..f55969368 100644 --- a/snovault/tests/test_drs.py +++ b/snovault/tests/test_drs.py @@ -73,7 +73,7 @@ def test_drs_get_object_failure(self, testapp, testing_download): # noQA fixtur with pytest.raises(Exception): testapp.get(f'/ga4gh/drs/v1/objects/{drs_object_uri}/accesss/https') - def test_drs_get_object_returns_json(self, testapp, htmltestapp, testing_download): + def test_drs_get_object_returns_json(self, testapp, htmltestapp, testing_download): # noQA fixture """ Tests that even with an htmltestapp, JSON is returned """ res = testapp.get(testing_download) drs_object_uri = res.json['uuid'] From 0ce2041e144969b38af95632ae9154ccc1871e43 Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Fri, 7 Jun 2024 14:44:47 -0400 Subject: [PATCH 07/18] remove auth check on route --- CHANGELOG.rst | 6 ++++++ pyproject.toml | 2 +- snovault/drs.py | 15 +++------------ 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9c4442498..6317f9923 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,12 @@ snovault Change Log ---------- +11.15.1 +======= + +* Update ``drs_download`` to not guard on Authentication, as this check is superfluous since @@drs as_user is evaluated + + 11.15.0 ======= diff --git a/pyproject.toml b/pyproject.toml index 243d05c7c..6ee039bf1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dcicsnovault" -version = "11.15.0" +version = "11.15.1" description = "Storage support for 4DN Data Portals." authors = ["4DN-DCIC Team "] license = "MIT" diff --git a/snovault/drs.py b/snovault/drs.py index 0239f7760..9b7664a61 100644 --- a/snovault/drs.py +++ b/snovault/drs.py @@ -101,18 +101,9 @@ def drs_objects(context, request): return formatted_drs_object -@view_config( - route_name='drs_download_no_slash', request_method='GET', - effective_principals=Authenticated -) -@view_config( - route_name='drs_download_slash', request_method='GET', - effective_principals=Authenticated -) -@view_config( - route_name='drs_download', request_method='GET', - effective_principals=Authenticated -) +@view_config(route_name='drs_download_no_slash', request_method='GET') +@view_config(route_name='drs_download_slash', request_method='GET') +@view_config(route_name='drs_download', request_method='GET') @debug_log def drs_objects_download(context, request): """ Implements DRS GET bytes as specified by the API description From feb13fe9bd5fb0112c8f9be01ba85c38d848fc3a Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Fri, 7 Jun 2024 14:52:59 -0400 Subject: [PATCH 08/18] remove unused import --- snovault/drs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/snovault/drs.py b/snovault/drs.py index 9b7664a61..4b40e596d 100644 --- a/snovault/drs.py +++ b/snovault/drs.py @@ -1,5 +1,4 @@ from pyramid.view import view_config -from pyramid.security import Authenticated from pyramid.exceptions import HTTPNotFound from .util import debug_log From dc0a67a15d8c477483dbcb690f1c53ef4024305e Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Wed, 26 Jun 2024 13:31:15 -0400 Subject: [PATCH 09/18] remove drs_id validation --- CHANGELOG.rst | 6 ++++++ pyproject.toml | 2 +- snovault/drs.py | 1 - 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6317f9923..12c859981 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,12 @@ snovault Change Log ---------- +11.15.2 +======= + +* Update ``drs`` validation to remove drs_uri + + 11.15.1 ======= diff --git a/pyproject.toml b/pyproject.toml index 6ee039bf1..f0e201e6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dcicsnovault" -version = "11.15.1" +version = "11.15.2" description = "Storage support for 4DN Data Portals." authors = ["4DN-DCIC Team "] license = "MIT" diff --git a/snovault/drs.py b/snovault/drs.py index 4b40e596d..fd34d4865 100644 --- a/snovault/drs.py +++ b/snovault/drs.py @@ -12,7 +12,6 @@ REQUIRED_FIELDS = [ 'id', 'created_time', - 'drs_id', 'self_uri', 'size', 'checksums' From 1b0e6d57596a96f73deee5617cb834ac5ae2a19f Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Thu, 27 Jun 2024 10:46:48 -0400 Subject: [PATCH 10/18] refactor unique key usage for base compatibility with drs --- snovault/resources.py | 11 +++++++- snovault/test_schemas/TestingDownload.json | 6 ++++- snovault/test_schemas/mixins.json | 30 +++++++++++++++++++++- snovault/tests/test_drs.py | 5 ++-- snovault/tests/testing_views.py | 16 +++--------- 5 files changed, 51 insertions(+), 17 deletions(-) diff --git a/snovault/resources.py b/snovault/resources.py index 68f7f36b9..236383a68 100644 --- a/snovault/resources.py +++ b/snovault/resources.py @@ -528,10 +528,19 @@ def rev_link_atids(self, request, rev_name): self.get_filtered_rev_links(request, rev_name)] def unique_keys(self, properties): - return { + """ This function used to only resolve keys from schema, it has been + updated to handle both accession and alternate_accession + """ + keys = { name: [v for prop in props for v in ensurelist(properties.get(prop, ()))] for name, props in self.type_info.schema_keys.items() } + if 'accession' not in self.schema['properties']: + return keys + keys.setdefault('accession', []).extend(properties.get('alternate_accessions', [])) + if properties.get('status') != 'replaced' and 'accession' in properties: + keys['accession'].append(properties['accession']) + return keys def add_accession_to_title(self, title): if self.properties.get('accession') is not None: diff --git a/snovault/test_schemas/TestingDownload.json b/snovault/test_schemas/TestingDownload.json index 2e21ec940..f9ffca40c 100644 --- a/snovault/test_schemas/TestingDownload.json +++ b/snovault/test_schemas/TestingDownload.json @@ -3,7 +3,8 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "mixinProperties": [ { "$ref": "mixins.json#/status" }, - { "$ref": "mixins.json#/submitted" } + { "$ref": "mixins.json#/submitted" }, + { "$ref": "mixins.json#/accession" } ], "properties": { "attachment": { @@ -25,6 +26,9 @@ "enum": ["image/png"] } } + }, + "accession": { + "accessionType": "TD" } } } \ No newline at end of file diff --git a/snovault/test_schemas/mixins.json b/snovault/test_schemas/mixins.json index cc74c163a..6f0aa2b7a 100644 --- a/snovault/test_schemas/mixins.json +++ b/snovault/test_schemas/mixins.json @@ -69,5 +69,33 @@ "pattern": "^[a-zA-Z0-9_\\-][a-zA-Z0-9_\\-\\s]+[a-zA-Z0-9_\\-]$" } } - } + }, + "accession": { + "accession": { + "title": "Accession", + "description": "A unique identifier to be used to reference the object.", + "internal_comment": "Only admins are allowed to set or update this value.", + "exclude_from": [ + "FFedit-create" + ], + "type": "string", + "format": "accession", + "permission": "restricted_fields", + "serverDefault": "accession" + }, + "alternate_accessions": { + "title": "Alternate Accessions", + "description": "Accessions previously assigned to objects that have been merged with this object.", + "type": "array", + "lookup": 1000, + "internal_comment": "Only admins are allowed to set or update this value.", + "items": { + "title": "Alternate Accession", + "description": "An accession previously assigned to an object that has been merged with this object.", + "type": "string", + "permission": "restricted_fields", + "format": "accession" + } + } + } } diff --git a/snovault/tests/test_drs.py b/snovault/tests/test_drs.py index f55969368..2ca8360cb 100644 --- a/snovault/tests/test_drs.py +++ b/snovault/tests/test_drs.py @@ -12,13 +12,14 @@ class TestDRSAPI: def test_drs_get_object(self, testapp, testing_download): # noQA fixture """ Tests basic structure about a drs object """ res = testapp.get(testing_download) - drs_object_uri = res.json['uuid'] + drs_object_uri = res.json['accession'] + drs_object_uuid = res.json['uuid'] drs_object_1 = testapp.get(f'/ga4gh/drs/v1/objects/{drs_object_uri}').json for key in REQUIRED_FIELDS: assert key in drs_object_1 assert drs_object_1['self_uri'] == f'drs://localhost:80/ga4gh/drs/v1/objects/{drs_object_uri}' assert (drs_object_1['access_methods'][0]['access_url']['url'] - == f'{self.BASE_URL}{drs_object_uri}/@@download') + == f'{self.BASE_URL}{drs_object_uuid}/@@download') # failure cases testapp.get(f'/ga4gh/drs/v1/objects/not_a_uri', status=404) diff --git a/snovault/tests/testing_views.py b/snovault/tests/testing_views.py index 2cae26961..012015d89 100644 --- a/snovault/tests/testing_views.py +++ b/snovault/tests/testing_views.py @@ -228,15 +228,6 @@ def __ac_local_roles__(self): # roles[viewing_group_members] = 'role.viewing_group_member' return roles - def unique_keys(self, properties): - keys = super(Item, self).unique_keys(properties) - if 'accession' not in self.schema['properties']: - return keys - keys.setdefault('accession', []).extend(properties.get('alternate_accessions', [])) - if properties.get('status') != 'replaced' and 'accession' in properties: - keys['accession'].append(properties['accession']) - return keys - @calculated_property(schema={ "title": "Display Title", "description": "A calculated title for every object in 4DN", @@ -418,6 +409,7 @@ class NestedObjectLinkTarget(Item): @collection( 'testing-downloads', + unique_key='accession', properties={ 'title': 'Test download collection', 'description': 'Testing. Testing. 1, 2, 3.', @@ -436,11 +428,11 @@ def drs(context, request): downstream API (see drs.py). """ rendered_object = request.embed(str(context.uuid), '@@object', as_user=True) + accession = rendered_object['accession'] drs_object = { - 'id': rendered_object['@id'], + 'id': accession, 'created_time': rendered_object['date_created'], - 'drs_id': rendered_object['uuid'], - 'self_uri': f'drs://{request.host}{request.path}', + 'self_uri': f'drs://{request.host}/{accession}', 'size': 0, 'checksums': [ { From d1902187c0586accf5ba734e6b7476aba664d215 Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Thu, 27 Jun 2024 12:39:58 -0400 Subject: [PATCH 11/18] push beta --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fa6ad809f..3bc841cf4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dcicsnovault" -version = "11.16.1" +version = "11.16.0.1b0" description = "Storage support for 4DN Data Portals." authors = ["4DN-DCIC Team "] license = "MIT" From b3dbb8dc14cfc9eb82b09a631200b17a8b8a312b Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Fri, 16 Aug 2024 13:54:30 -0400 Subject: [PATCH 12/18] repair drs for 1.3 --- snovault/drs.py | 13 +++++++++++++ snovault/tests/test_drs.py | 6 ++++++ snovault/tests/testing_views.py | 3 ++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/snovault/drs.py b/snovault/drs.py index fd34d4865..e9ca03796 100644 --- a/snovault/drs.py +++ b/snovault/drs.py @@ -1,10 +1,13 @@ from pyramid.view import view_config from pyramid.exceptions import HTTPNotFound +from pyramid.response import Response +from dcicutils.misc_utils import ignored from .util import debug_log DRS_VERSION_1 = 'v1' DRS_PREFIX_V1 = f'ga4gh/drs/{DRS_VERSION_1}' +DRS_OBJECT_OPTIONS = DRS_PREFIX_V1 + '/options' DRS_OBJECT_GET = DRS_PREFIX_V1 + '/objects/{object_id}' DRS_OBJECT_GET_ACCESS_URL = DRS_PREFIX_V1 + '/objects/{object_id}/access/{access_id}' DRS_OBJECT_GET_ACCESSS_URL_SLASH = DRS_PREFIX_V1 + '/objects/{object_id}/access/' @@ -23,6 +26,7 @@ def includeme(config): + config.add_route('drs_options', '/' + DRS_OBJECT_OPTIONS) config.add_route('drs_objects', '/' + DRS_OBJECT_GET) config.add_route('drs_download', '/' + DRS_OBJECT_GET_ACCESS_URL) config.add_route('drs_download_slash', '/' + DRS_OBJECT_GET_ACCESSS_URL_SLASH) @@ -81,6 +85,15 @@ def get_drs_url(request, object_uri): f' did not pass valid access_id or does not exist {str(e)}') +@view_config( + route_name='drs_options', request_method='OPTIONS' +) +@debug_log +def drs_options(context, request): + ignored(context), ignored(request) + return Response(status_code=204) + + @view_config( route_name='drs_objects', request_method='GET' ) diff --git a/snovault/tests/test_drs.py b/snovault/tests/test_drs.py index 2ca8360cb..b6c1269a3 100644 --- a/snovault/tests/test_drs.py +++ b/snovault/tests/test_drs.py @@ -9,6 +9,11 @@ class TestDRSAPI: """ BASE_URL = 'http://localhost:80/' + def test_drs_options(self, testapp): + """ Tests that the options endpoint returns 204 """ + res = testapp.options('/ga4gh/drs/v1/options', headers={'Content-Type': 'application/json'}) + assert res.status_code == 204 + def test_drs_get_object(self, testapp, testing_download): # noQA fixture """ Tests basic structure about a drs object """ res = testapp.get(testing_download) @@ -20,6 +25,7 @@ def test_drs_get_object(self, testapp, testing_download): # noQA fixture assert drs_object_1['self_uri'] == f'drs://localhost:80/ga4gh/drs/v1/objects/{drs_object_uri}' assert (drs_object_1['access_methods'][0]['access_url']['url'] == f'{self.BASE_URL}{drs_object_uuid}/@@download') + assert (drs_object_1['access_methods'][0]['access_id'] == 'http') # failure cases testapp.get(f'/ga4gh/drs/v1/objects/not_a_uri', status=404) diff --git a/snovault/tests/testing_views.py b/snovault/tests/testing_views.py index 012015d89..e191a71b3 100644 --- a/snovault/tests/testing_views.py +++ b/snovault/tests/testing_views.py @@ -445,7 +445,8 @@ def drs(context, request): 'access_url': { 'url': f'http://{request.host}/{context.uuid}/@@download' }, - 'type': 'http' + 'type': 'http', + 'access_id': 'http' }, ] } From 814e2e0e6846f47227b63d9ff5c5d17f3feb49a7 Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Fri, 16 Aug 2024 13:54:46 -0400 Subject: [PATCH 13/18] version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1e3cb7162..a879cbf45 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dcicsnovault" -version = "11.21.1.0b0" +version = "11.21.1.0b1" description = "Storage support for 4DN Data Portals." authors = ["4DN-DCIC Team "] license = "MIT" From b83041f877ea63051580b85938f6aab6f3480033 Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Tue, 27 Aug 2024 13:59:16 -0400 Subject: [PATCH 14/18] always reference drs id (accession) in uri --- pyproject.toml | 2 +- snovault/drs.py | 3 ++- snovault/tests/test_drs.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a879cbf45..f99f23462 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dcicsnovault" -version = "11.21.1.0b1" +version = "11.21.1.0b2" description = "Storage support for 4DN Data Portals." authors = ["4DN-DCIC Team "] license = "MIT" diff --git a/snovault/drs.py b/snovault/drs.py index e9ca03796..52591d7e5 100644 --- a/snovault/drs.py +++ b/snovault/drs.py @@ -57,7 +57,8 @@ def get_and_format_drs_object(request, object_uri): except Exception: raise HTTPNotFound('You accessed a DRS object_uri that either does not exist' ' or you do not have access to it.') - drs_object['self_uri'] = f'drs://{request.host}{request.path}' + uri = drs_object['id'] + drs_object['self_uri'] = f'drs://{request.host}/{uri}' return drs_object diff --git a/snovault/tests/test_drs.py b/snovault/tests/test_drs.py index b6c1269a3..806104123 100644 --- a/snovault/tests/test_drs.py +++ b/snovault/tests/test_drs.py @@ -22,7 +22,7 @@ def test_drs_get_object(self, testapp, testing_download): # noQA fixture drs_object_1 = testapp.get(f'/ga4gh/drs/v1/objects/{drs_object_uri}').json for key in REQUIRED_FIELDS: assert key in drs_object_1 - assert drs_object_1['self_uri'] == f'drs://localhost:80/ga4gh/drs/v1/objects/{drs_object_uri}' + assert drs_object_1['self_uri'] == f'drs://localhost:80/{drs_object_uri}' assert (drs_object_1['access_methods'][0]['access_url']['url'] == f'{self.BASE_URL}{drs_object_uuid}/@@download') assert (drs_object_1['access_methods'][0]['access_id'] == 'http') From 79f0606aaba91a8e6e862da041d48eef82689584 Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Wed, 28 Aug 2024 14:06:48 -0400 Subject: [PATCH 15/18] allow object_id for options --- pyproject.toml | 2 +- snovault/drs.py | 2 +- snovault/tests/test_drs.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f99f23462..8398d0469 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dcicsnovault" -version = "11.21.1.0b2" +version = "11.21.1.0b3" description = "Storage support for 4DN Data Portals." authors = ["4DN-DCIC Team "] license = "MIT" diff --git a/snovault/drs.py b/snovault/drs.py index 52591d7e5..b71423002 100644 --- a/snovault/drs.py +++ b/snovault/drs.py @@ -7,7 +7,7 @@ DRS_VERSION_1 = 'v1' DRS_PREFIX_V1 = f'ga4gh/drs/{DRS_VERSION_1}' -DRS_OBJECT_OPTIONS = DRS_PREFIX_V1 + '/options' +DRS_OBJECT_OPTIONS = DRS_PREFIX_V1 + '/options/{object_id}' DRS_OBJECT_GET = DRS_PREFIX_V1 + '/objects/{object_id}' DRS_OBJECT_GET_ACCESS_URL = DRS_PREFIX_V1 + '/objects/{object_id}/access/{access_id}' DRS_OBJECT_GET_ACCESSS_URL_SLASH = DRS_PREFIX_V1 + '/objects/{object_id}/access/' diff --git a/snovault/tests/test_drs.py b/snovault/tests/test_drs.py index 806104123..f1b191fe0 100644 --- a/snovault/tests/test_drs.py +++ b/snovault/tests/test_drs.py @@ -11,7 +11,7 @@ class TestDRSAPI: def test_drs_options(self, testapp): """ Tests that the options endpoint returns 204 """ - res = testapp.options('/ga4gh/drs/v1/options', headers={'Content-Type': 'application/json'}) + res = testapp.options('/ga4gh/drs/v1/options/blah', headers={'Content-Type': 'application/json'}) assert res.status_code == 204 def test_drs_get_object(self, testapp, testing_download): # noQA fixture From d8036a6a6cf32d1a4271d96b598d3847fbd2c6e9 Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Wed, 28 Aug 2024 14:53:01 -0400 Subject: [PATCH 16/18] fix options path --- pyproject.toml | 2 +- snovault/drs.py | 15 +++------------ snovault/tests/test_drs.py | 7 ++----- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8398d0469..884a1e049 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dcicsnovault" -version = "11.21.1.0b3" +version = "11.21.1.0b4" description = "Storage support for 4DN Data Portals." authors = ["4DN-DCIC Team "] license = "MIT" diff --git a/snovault/drs.py b/snovault/drs.py index b71423002..67d98ce14 100644 --- a/snovault/drs.py +++ b/snovault/drs.py @@ -7,7 +7,6 @@ DRS_VERSION_1 = 'v1' DRS_PREFIX_V1 = f'ga4gh/drs/{DRS_VERSION_1}' -DRS_OBJECT_OPTIONS = DRS_PREFIX_V1 + '/options/{object_id}' DRS_OBJECT_GET = DRS_PREFIX_V1 + '/objects/{object_id}' DRS_OBJECT_GET_ACCESS_URL = DRS_PREFIX_V1 + '/objects/{object_id}/access/{access_id}' DRS_OBJECT_GET_ACCESSS_URL_SLASH = DRS_PREFIX_V1 + '/objects/{object_id}/access/' @@ -26,7 +25,6 @@ def includeme(config): - config.add_route('drs_options', '/' + DRS_OBJECT_OPTIONS) config.add_route('drs_objects', '/' + DRS_OBJECT_GET) config.add_route('drs_download', '/' + DRS_OBJECT_GET_ACCESS_URL) config.add_route('drs_download_slash', '/' + DRS_OBJECT_GET_ACCESSS_URL_SLASH) @@ -87,22 +85,15 @@ def get_drs_url(request, object_uri): @view_config( - route_name='drs_options', request_method='OPTIONS' -) -@debug_log -def drs_options(context, request): - ignored(context), ignored(request) - return Response(status_code=204) - - -@view_config( - route_name='drs_objects', request_method='GET' + route_name='drs_objects', request_method=['GET', 'OPTIONS'] ) @debug_log def drs_objects(context, request): """ Implements DRS GET as specified by the API description https://ga4gh.github.io/data-repository-service-schemas/preview/release/drs-1.0.0/docs/#_getobject """ + if request.method == 'OPTIONS': + return Response(status_code=204) drs_object_uri = '/' + request.matchdict['object_id'] formatted_drs_object = get_and_format_drs_object(request, drs_object_uri) try: diff --git a/snovault/tests/test_drs.py b/snovault/tests/test_drs.py index f1b191fe0..dc39b72f6 100644 --- a/snovault/tests/test_drs.py +++ b/snovault/tests/test_drs.py @@ -9,16 +9,13 @@ class TestDRSAPI: """ BASE_URL = 'http://localhost:80/' - def test_drs_options(self, testapp): - """ Tests that the options endpoint returns 204 """ - res = testapp.options('/ga4gh/drs/v1/options/blah', headers={'Content-Type': 'application/json'}) - assert res.status_code == 204 - def test_drs_get_object(self, testapp, testing_download): # noQA fixture """ Tests basic structure about a drs object """ res = testapp.get(testing_download) drs_object_uri = res.json['accession'] drs_object_uuid = res.json['uuid'] + testapp.options(f'/ga4gh/drs/v1/objects/{drs_object_uri}', + headers={'Content-Type': 'application/json'}, status=204) drs_object_1 = testapp.get(f'/ga4gh/drs/v1/objects/{drs_object_uri}').json for key in REQUIRED_FIELDS: assert key in drs_object_1 From 03092f1e40a2192a071cc5f43a729acf3ef70ed8 Mon Sep 17 00:00:00 2001 From: William Ronchetti Date: Thu, 29 Aug 2024 09:25:38 -0400 Subject: [PATCH 17/18] do not check content type for options since unused --- pyproject.toml | 2 +- snovault/renderers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 884a1e049..7c33cef98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dcicsnovault" -version = "11.21.1.0b4" +version = "11.21.1.0b5" description = "Storage support for 4DN Data Portals." authors = ["4DN-DCIC Team "] license = "MIT" diff --git a/snovault/renderers.py b/snovault/renderers.py index 047a6e02f..7935cafa3 100644 --- a/snovault/renderers.py +++ b/snovault/renderers.py @@ -110,7 +110,7 @@ def validate_request_tween(request): if 'X_REQUEST_METHOD' in environ: environ['REQUEST_METHOD'] = environ['X_REQUEST_METHOD'] - if request.method in ('GET', 'HEAD'): + if request.method in ('GET', 'HEAD', 'OPTIONS'): # If GET request, don't need to check `request.content_type` # Includes page text/html requests. return handler(request) From 44205568f133c6c8cba7dae230f9c500945953d4 Mon Sep 17 00:00:00 2001 From: David Michaels Date: Wed, 9 Oct 2024 22:01:50 -0400 Subject: [PATCH 18/18] lint fix --- snovault/drs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/snovault/drs.py b/snovault/drs.py index 67d98ce14..e73694e39 100644 --- a/snovault/drs.py +++ b/snovault/drs.py @@ -1,7 +1,6 @@ from pyramid.view import view_config from pyramid.exceptions import HTTPNotFound from pyramid.response import Response -from dcicutils.misc_utils import ignored from .util import debug_log