diff --git a/config/dev.yml b/config/dev.yml --- a/config/dev.yml +++ b/config/dev.yml @@ -7,5 +7,3 @@ url: http://moma.internal.softwareheritage.org:5010 debug: yes - -server-type: asgi diff --git a/config/staging.yml b/config/staging.yml --- a/config/staging.yml +++ b/config/staging.yml @@ -7,5 +7,3 @@ url: http://webapp.internal.staging.swh.network:5010 debug: no - -server-type: asgi diff --git a/swh/graphql/backends/archive.py b/swh/graphql/backends/archive.py --- a/swh/graphql/backends/archive.py +++ b/swh/graphql/backends/archive.py @@ -15,7 +15,6 @@ OriginVisitStatus, Release, Revision, - Sha1, Sha1Git, ) from swh.model.swhids import ObjectType @@ -143,5 +142,5 @@ def get_contents(self, checksums: Dict[str, Any]) -> List[Content]: return self.storage.content_find(content=checksums) - def get_content_data(self, content_sha1: Sha1) -> Optional[bytes]: - return self.storage.content_get_data(content=content_sha1) + # def get_content_data(self, content_sha1: Sha1) -> Optional[bytes]: + # return self.storage.content_get_data(content=content_sha1) diff --git a/swh/graphql/resolvers/person.py b/swh/graphql/resolvers/person.py --- a/swh/graphql/resolvers/person.py +++ b/swh/graphql/resolvers/person.py @@ -2,9 +2,3 @@ # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information - -from .base_node import BaseNode - - -class PersonNode(BaseNode): - """ """ diff --git a/swh/graphql/resolvers/scalars.py b/swh/graphql/resolvers/scalars.py --- a/swh/graphql/resolvers/scalars.py +++ b/swh/graphql/resolvers/scalars.py @@ -21,10 +21,10 @@ @id_scalar.serializer -def serialize_id(value): - if type(value) is bytes: - return value.hex() - return value +def serialize_id(value) -> str: + if type(value) is str: + value = value.encode() + return value.hex() @datetime_scalar.serializer diff --git a/swh/graphql/server.py b/swh/graphql/server.py --- a/swh/graphql/server.py +++ b/swh/graphql/server.py @@ -64,6 +64,7 @@ SWH_CONFIG_FILENAME environment variable defines the configuration path to load. """ + from ariadne.asgi import GraphQL from starlette.middleware.cors import CORSMiddleware from .app import schema @@ -75,14 +76,10 @@ config_path = os.environ.get("SWH_CONFIG_FILENAME") graphql_cfg = load_and_check_config(config_path) - server_type = graphql_cfg.get("server-type") - if server_type == "asgi": - from ariadne.asgi import GraphQL - - application = CORSMiddleware( - GraphQL(schema, debug=graphql_cfg["debug"], error_formatter=format_error), - # FIXME, restrict origins after deploying the JS client - allow_origins=["*"], - allow_methods=("GET", "POST", "OPTIONS"), - ) + application = CORSMiddleware( + GraphQL(schema, debug=graphql_cfg["debug"], error_formatter=format_error), + # FIXME, restrict origins after deploying the JS client + allow_origins=["*"], + allow_methods=("GET", "POST", "OPTIONS"), + ) return application diff --git a/swh/graphql/tests/functional/test_content.py b/swh/graphql/tests/functional/test_content.py --- a/swh/graphql/tests/functional/test_content.py +++ b/swh/graphql/tests/functional/test_content.py @@ -15,6 +15,7 @@ query getContent($swhid: SWHID!) { content(swhid: $swhid) { swhid + id checksum { blake2s256 sha1 @@ -42,6 +43,7 @@ archive_url = "https://archive.softwareheritage.org/api/1/" response = { "swhid": str(content.swhid()), + "id": content.sha1_git.hex(), "checksum": { "blake2s256": content.blake2s256.hex(), "sha1": content.sha1.hex(), diff --git a/swh/graphql/tests/unit/resolvers/test_resolver_factory.py b/swh/graphql/tests/unit/resolvers/test_resolver_factory.py --- a/swh/graphql/tests/unit/resolvers/test_resolver_factory.py +++ b/swh/graphql/tests/unit/resolvers/test_resolver_factory.py @@ -15,4 +15,4 @@ def test_get_connection_resolver_invalid_type(self): with pytest.raises(AttributeError): - resolver_factory.get_connection_resolver("invalid", None, None) + resolver_factory.ConnectionObjectFactory().create("invalid", None, None) diff --git a/swh/graphql/tests/unit/resolvers/test_scalars.py b/swh/graphql/tests/unit/resolvers/test_scalars.py new file mode 100644 --- /dev/null +++ b/swh/graphql/tests/unit/resolvers/test_scalars.py @@ -0,0 +1,54 @@ +# Copyright (C) 2022 The Software Heritage developers +# See the AUTHORS file at the top-level directory of this distribution +# License: GNU General Public License version 3, or any later version +# See top-level LICENSE file for more information + +import datetime + +import pytest + +from swh.graphql.errors import InvalidInputError +from swh.graphql.resolvers import scalars + + +def test_serialize_id(): + assert scalars.serialize_id("test") == "74657374" + assert scalars.serialize_id(b"test") == "74657374" + + +def test_serialize_datetime(): + assert scalars.serialize_datetime("invalid") is None + # python datetime + date = datetime.datetime(2020, 5, 17) + assert scalars.serialize_datetime(date) == date.isoformat() + # FIXME, Timestamp with timezone + + +def test_validate_swhid_invalid(): + with pytest.raises(InvalidInputError): + scalars.validate_swhid("invalid") + + +def test_validate_swhid(): + swhid = scalars.validate_swhid(f"swh:1:rev:{'1' * 40}") + assert str(swhid) == "swh:1:rev:1111111111111111111111111111111111111111" + + +@pytest.mark.parametrize("content_hash", ["invalid", "test:invalid"]) +def test_validate_content_hash_invalid_value(content_hash): + with pytest.raises(InvalidInputError) as e: + scalars.validate_content_hash(content_hash) + assert "Invalid content checksum" in str(e.value) + + +def test_validate_content_hash_invalid_hash_algo(): + with pytest.raises(InvalidInputError) as e: + scalars.validate_content_hash(f"invalid:{'1' * 40}") + assert "Invalid hash algorithm" in str(e.value) + + +def test_validate_content_hash(): + assert ( + "sha1", + b"\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11", + ) == scalars.validate_content_hash(f"sha1:{'1' * 40}") diff --git a/swh/graphql/tests/unit/test_server.py b/swh/graphql/tests/unit/test_server.py new file mode 100644 --- /dev/null +++ b/swh/graphql/tests/unit/test_server.py @@ -0,0 +1,67 @@ +# Copyright (C) 2022 The Software Heritage developers +# See the AUTHORS file at the top-level directory of this distribution +# License: GNU General Public License version 3, or any later version +# See top-level LICENSE file for more information + +import pytest + +from swh.graphql import server + + +def test_get_storage(mocker): + server.storage = None + server.graphql_cfg = {"storage": {"test": "test"}} + mocker.patch("swh.graphql.server.get_swh_storage", return_value="dummy-storage") + assert server.get_storage() == "dummy-storage" + + +def test_get_global_storage(mocker): + server.storage = "existing-storage" + assert server.get_storage() == "existing-storage" + + +def test_get_search(mocker): + server.search = None + server.graphql_cfg = {"search": {"test": "test"}} + mocker.patch("swh.graphql.server.get_swh_search", return_value="dummy-search") + assert server.get_search() == "dummy-search" + + +def test_get_global_search(mocker): + server.search = "existing-search" + assert server.get_search() == "existing-search" + + +def test_load_and_check_config_no_config(): + with pytest.raises(EnvironmentError): + server.load_and_check_config(config_path=None) + + +def test_load_and_check_config_missing_config_file(): + with pytest.raises(FileNotFoundError): + server.load_and_check_config(config_path="invalid") + + +def test_load_and_check_config_missing_storage_config(mocker): + mocker.patch("swh.core.config.read", return_value={"test": "test"}) + with pytest.raises(KeyError): + server.load_and_check_config(config_path="/tmp") + + +def test_load_and_check_config(mocker): + mocker.patch("swh.core.config.read", return_value={"storage": {"test": "test"}}) + cfg = server.load_and_check_config(config_path="/tmp") + assert cfg == {"storage": {"test": "test"}} + + +def test_make_app_from_configfile_with_config(mocker): + server.graphql_cfg = {"storage": {"test": "test"}, "debug": True} + mocker.patch("starlette.middleware.cors.CORSMiddleware", return_value="dummy-app") + assert server.make_app_from_configfile() == "dummy-app" + + +def test_make_app_from_configfile_missing_config(mocker): + server.graphql_cfg = None + with pytest.raises(EnvironmentError): + # trying to load config from a non existing env var + assert server.make_app_from_configfile()