From dc0e0b722b649fde87ad345c65c30ff1daa501f3 Mon Sep 17 00:00:00 2001 From: Marion Deveaud Date: Thu, 28 Mar 2024 17:22:38 +0100 Subject: [PATCH] fix(version): use v1 by default until v2 will be released --- README.rst | 12 ++++---- docs-source/conf.py | 2 +- fossology/__init__.py | 10 +++---- fossology/uploads.py | 56 ++++++++++++++++++++++-------------- pyproject.toml | 2 +- tests/conftest.py | 16 +++++------ tests/test_init.py | 28 ++++++++++++++++-- tests/test_items.py | 6 ++-- tests/test_upload_from.py | 60 +++++++++++++++++++++++++++++++++++++++ tests/test_uploads.py | 47 +++++++++++++++++++----------- 10 files changed, 175 insertions(+), 64 deletions(-) diff --git a/README.rst b/README.rst index 11fff55..fe612fd 100644 --- a/README.rst +++ b/README.rst @@ -26,13 +26,13 @@ A simple wrapper for the Fossology REST API. See `the OpenAPI specification `_ used to implement this library. -Current release is compatible with **Fossology version 4.4.0-rc2** - API version 2.0.0 (not all endpoints are supported) +Current release is compatible with **Fossology version 4.4.0** - API version 1.6.1 (not all endpoints are supported) `See release notes `_ for all details. If you miss an API Endpoint, please open a new issue or contribute a pull request. - API v1 is supported too, it needs to be specified explicitly. + API v2 is partially supported too, however the specification is not stable yet and not all endpoints are supported. Documentation ============= @@ -68,14 +68,14 @@ Using the API FOSSOLOGY_PASSWORD = "fossy" TOKEN_NAME = "fossy_token" - # By default version v2 of the token generation API will be used + # By default version v1 of the token generation API will be used token = fossology_token( FOSSOLOGY_SERVER, FOSSOLOGY_USER, FOSSOLOGY_PASSWORD, TOKEN_NAME, TokenScope.WRITE - version="v2" + version="v1" ) - Start using the API: @@ -84,8 +84,8 @@ Using the API from fossology import Fossology - # By default version v2 of the API will be used - foss = Fossology(FOSSOLOGY_SERVER, token, FOSSOLOGY_USER, version="v2") + # By default version v1 of the API will be used + foss = Fossology(FOSSOLOGY_SERVER, token, FOSSOLOGY_USER, version="v1") print(f"Logged in as user {foss.user.name}") diff --git a/docs-source/conf.py b/docs-source/conf.py index a93f4da..02841f9 100644 --- a/docs-source/conf.py +++ b/docs-source/conf.py @@ -22,7 +22,7 @@ copyright = "2021, Siemens AG" # The full version, including major/minor/patch tags -release = "3.2.0" +release = "3.2.1" # -- General configuration --------------------------------------------------- diff --git a/fossology/__init__.py b/fossology/__init__.py index fa716c6..28d2f91 100644 --- a/fossology/__init__.py +++ b/fossology/__init__.py @@ -31,7 +31,7 @@ def fossology_token( token_name, token_scope=TokenScope.READ, token_expire=None, - version="v2", + version="v1", ): """Generate an API token using username/password @@ -41,7 +41,7 @@ def fossology_token( >>> from fossology import fossology_token # doctest: +SKIP >>> from fossology.obj import TokenScope # doctest: +SKIP - >>> token = fossology_token("https://fossology.example.com/repo", "Me", "MyPassword", "MyToken", version="v2") # doctest: +SKIP + >>> token = fossology_token("https://fossology.example.com/repo", "Me", "MyPassword", "MyToken", version="v1") # doctest: +SKIP :param url: the URL of the Fossology server @@ -50,7 +50,7 @@ def fossology_token( :param name: the name of the token :param scope: the scope of the token (default: TokenScope.READ) :param expire: the expire date of the token, e.g. 2019-12-25 (default: max. 30 days) - :param version: the version of the API to use (default: "v2") + :param version: the version of the API to use (default: "v1") :type url: string :type username: string :type password: string @@ -118,7 +118,7 @@ class Fossology( :param url: URL of the Fossology instance :param token: The API token generated using the Fossology UI - :param version: the version of the API to use (default: "v2") + :param version: the version of the API to use (default: "v1") :type url: str :type token: str :type version: str @@ -126,7 +126,7 @@ class Fossology( :raises AuthenticationError: if the user couldn't be authenticated """ - def __init__(self, url, token, version="v2"): + def __init__(self, url, token, version="v1"): self.host = url self.token = token self.users = list() diff --git a/fossology/uploads.py b/fossology/uploads.py index 20b9bf7..119ac67 100644 --- a/fossology/uploads.py +++ b/fossology/uploads.py @@ -156,7 +156,7 @@ def upload_file( >>> from fossology import Fossology >>> from fossology.enums import AccessLevel - >>> foss = Fossology(FOSS_URL, FOSS_TOKEN, username) # doctest: +SKIP + >>> foss = Fossology(FOSS_URL, FOSS_TOKEN) # doctest: +SKIP >>> my_upload = foss.upload_file( ... foss.rootFolder, ... file="my-package.zip", @@ -237,39 +237,53 @@ def upload_file( :raises FossologyApiError: if the REST call failed :raises AuthorizationError: if the REST call is not authorized """ - headers = {"folderId": str(folder.id)} - if description: - headers["uploadDescription"] = description - if access_level: - headers["public"] = access_level.value - if apply_global: - headers["applyGlobal"] = "true" - if ignore_scm: - headers["ignoreScm"] = "true" - if group: + data = { + "folderId": str(folder.id), + "uploadDescription": description, + "public": access_level.value + if access_level + else AccessLevel.PROTECTED.value, + "applyGlobal": apply_global, + "ignoreScm": ignore_scm, + "uploadType": "file", + } + + headers = {} + if "v1" in self.api: + headers = { + k: str(v).lower() if isinstance(v, bool) else v for k, v in data.items() + } # Needed for API v1.x headers["groupName"] = group + endpoint = f"{self.api}/uploads" + else: + if group: + endpoint = f"{self.api}/uploads?groupName={group}" + else: + endpoint = f"{self.api}/uploads" if file: - headers["uploadType"] = "file" + data["uploadType"] = headers["uploadType"] = "file" with open(file, "rb") as fp: files = {"fileInput": fp} response = self.session.post( - f"{self.api}/uploads", files=files, headers=headers + endpoint, files=files, headers=headers, data=data ) elif vcs or url or server: - data = dict if vcs: - headers["uploadType"] = "vcs" - data = {"location": vcs} # type: ignore + data["location"] = vcs + print(data) + data["uploadType"] = headers["uploadType"] = "vcs" elif url: - headers["uploadType"] = "url" - data = {"location": url} # type: ignore + data["location"] = url + data["uploadType"] = headers["uploadType"] = "url" elif server: - headers["uploadType"] = "server" - data = {"location": server} # type: ignore + data["location"] = server + data["uploadType"] = headers["uploadType"] = "server" headers["Content-Type"] = "application/json" response = self.session.post( - f"{self.api}/uploads", data=json.dumps(data), headers=headers + endpoint, + data=json.dumps(data), + headers=headers, ) else: logger.info( diff --git a/pyproject.toml b/pyproject.toml index 77707d5..cf95a89 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "fossology" -version = "3.2.0" +version = "3.2.1" description = "A library to automate Fossology from Python scripts" authors = ["Marion Deveaud "] license = "MIT License" diff --git a/tests/conftest.py b/tests/conftest.py index 41af50d..0c01fff 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -175,11 +175,11 @@ def foss(foss_server: str, foss_token: str, foss_agents: Agents) -> fossology.Fo @pytest.fixture(scope="session") -def foss_v1( +def foss_v2( foss_server: str, foss_token: str, foss_agents: Agents ) -> fossology.Fossology: try: - foss = fossology.Fossology(foss_server, foss_token, version="v1") + foss = fossology.Fossology(foss_server, foss_token, version="v2") except (FossologyApiError, AuthenticationError) as error: exit(error.message) @@ -231,20 +231,20 @@ def upload( @pytest.fixture(scope="function") -def upload_v1( - foss_v1: fossology.Fossology, +def upload_v2( + foss_v2: fossology.Fossology, test_file_path: str, ) -> Generator: - upload = foss_v1.upload_file( - foss_v1.rootFolder, + upload = foss_v2.upload_file( + foss_v2.rootFolder, file=test_file_path, description="Test upload via fossology-python lib", access_level=AccessLevel.PUBLIC, wait_time=5, ) - jobs_lookup(foss_v1, upload) + jobs_lookup(foss_v2, upload) yield upload - foss_v1.delete_upload(upload) + foss_v2.delete_upload(upload) time.sleep(5) diff --git a/tests/test_init.py b/tests/test_init.py index cf22e15..62e2278 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -15,12 +15,24 @@ def test_get_info(foss: Fossology): @responses.activate -def test_info_does_not_return_200(foss_server: str, foss: Fossology): +def test_info_v2_does_not_return_200(foss_server: str, foss_v2: Fossology): responses.add( responses.GET, f"{foss_server}/api/v2/info", status=400, ) + with pytest.raises(FossologyApiError) as excinfo: + foss_v2.get_info() + assert "Error while getting API info" in str(excinfo.value) + + +@responses.activate +def test_info_does_not_return_200(foss_server: str, foss: Fossology): + responses.add( + responses.GET, + f"{foss_server}/api/v1/info", + status=400, + ) with pytest.raises(FossologyApiError) as excinfo: foss.get_info() assert "Error while getting API info" in str(excinfo.value) @@ -36,9 +48,21 @@ def test_get_health(foss: Fossology): def test_health_does_not_return_200(foss_server: str, foss: Fossology): responses.add( responses.GET, - f"{foss_server}/api/v2/health", + f"{foss_server}/api/v1/health", status=503, ) with pytest.raises(FossologyApiError) as excinfo: foss.get_health() assert "Error while getting health info" in str(excinfo.value) + + +@responses.activate +def test_health_v2_does_not_return_200(foss_server: str, foss_v2: Fossology): + responses.add( + responses.GET, + f"{foss_server}/api/v2/health", + status=503, + ) + with pytest.raises(FossologyApiError) as excinfo: + foss_v2.get_health() + assert "Error while getting health info" in str(excinfo.value) diff --git a/tests/test_items.py b/tests/test_items.py index 4911a89..fc2eda2 100644 --- a/tests/test_items.py +++ b/tests/test_items.py @@ -16,9 +16,9 @@ def test_item_info(foss: Fossology, upload_with_jobs: Upload): assert info.meta_info -def test_item_info_v1(foss_v1: Fossology, upload_with_jobs: Upload): - files, _ = foss_v1.search(license="BSD") - info: FileInfo = foss_v1.item_info(upload_with_jobs, files[0].uploadTreeId) +def test_item_info_v2(foss_v2: Fossology, upload_with_jobs: Upload): + files, _ = foss_v2.search(license="BSD") + info: FileInfo = foss_v2.item_info(upload_with_jobs, files[0].uploadTreeId) assert info.meta_info diff --git a/tests/test_upload_from.py b/tests/test_upload_from.py index cad2855..30cf43d 100644 --- a/tests/test_upload_from.py +++ b/tests/test_upload_from.py @@ -43,6 +43,27 @@ def test_upload_from_vcs(foss: Fossology): delete_upload(foss, vcs_upload) +def test_upload_from_vcs_v2(foss_v2: Fossology): + vcs = { + "vcsType": "git", + "vcsUrl": "https://github.com/fossology/fossology-python", + "vcsName": "fossology-python-github-master", + "vcsUsername": "", + "vcsPassword": "", + } + vcs_upload = foss_v2.upload_file( + foss_v2.rootFolder, + vcs=vcs, + description="Test upload from github repository via python lib", + access_level=AccessLevel.PUBLIC, + ignore_scm=False, + wait_time=5, + ) + assert vcs_upload.uploadname == vcs["vcsName"] + # Cleanup + delete_upload(foss_v2, vcs_upload) + + def test_upload_from_url(foss: Fossology): url = { "url": "https://github.com/fossology/fossology-python/archive/master.zip", @@ -63,6 +84,26 @@ def test_upload_from_url(foss: Fossology): delete_upload(foss, url_upload) +def test_upload_from_url_v2(foss_v2: Fossology): + url = { + "url": "https://github.com/fossology/fossology-python/archive/master.zip", + "name": "fossology-python-master.zip", + "accept": "zip", + "reject": "", + "maxRecursionDepth": "1", + } + url_upload = foss_v2.upload_file( + foss_v2.rootFolder, + url=url, + description="Test upload from url via python lib", + access_level=AccessLevel.PUBLIC, + wait_time=5, + ) + assert url_upload.uploadname == url["name"] + # Cleanup + delete_upload(foss_v2, url_upload) + + def test_upload_from_server(foss: Fossology): server = { "path": "/tmp/base-files-11", @@ -80,3 +121,22 @@ def test_upload_from_server(foss: Fossology): # Cleanup delete_upload(foss, server_upload) + + +def test_upload_from_server_v2(foss_v2: Fossology): + server = { + "path": "/tmp/base-files-11", + "name": "base-files-11", + } + server_upload = foss_v2.upload_file( + foss_v2.rootFolder, + server=server, + description="Test upload from server via python lib", + access_level=AccessLevel.PUBLIC, + apply_global=True, + wait_time=5, + ) + assert server_upload.uploadname == server["name"] + + # Cleanup + delete_upload(foss_v2, server_upload) diff --git a/tests/test_uploads.py b/tests/test_uploads.py index 8a08c3e..5a2c695 100644 --- a/tests/test_uploads.py +++ b/tests/test_uploads.py @@ -30,16 +30,29 @@ def test_upload_sha1(upload: Upload): ) -def test_upload_v1(upload_v1: Upload): - assert upload_v1.uploadname == "base-files_11.tar.xz" - assert upload_v1.hash.sha1 == "D4D663FC2877084362FB2297337BE05684869B00" - assert str(upload_v1) == ( - f"Upload '{upload_v1.uploadname}' ({upload_v1.id}, {upload_v1.hash.size}B, {upload_v1.hash.sha1}) " - f"in folder {upload_v1.foldername} ({upload_v1.folderid})" +def test_upload(upload: Upload): + assert upload.uploadname == "base-files_11.tar.xz" + assert upload.hash.sha1 == "D4D663FC2877084362FB2297337BE05684869B00" + assert str(upload) == ( + f"Upload '{upload.uploadname}' ({upload.id}, {upload.hash.size}B, {upload.hash.sha1}) " + f"in folder {upload.foldername} ({upload.folderid})" + ) + assert str(upload.hash) == ( + f"File SHA1: {upload.hash.sha1} MD5 {upload.hash.md5} " + f"SH256 {upload.hash.sha256} Size {upload.hash.size}B" + ) + + +def test_upload_v2(upload_v2: Upload): + assert upload_v2.uploadname == "base-files_11.tar.xz" + assert upload_v2.hash.sha1 == "D4D663FC2877084362FB2297337BE05684869B00" + assert str(upload_v2) == ( + f"Upload '{upload_v2.uploadname}' ({upload_v2.id}, {upload_v2.hash.size}B, {upload_v2.hash.sha1}) " + f"in folder {upload_v2.foldername} ({upload_v2.folderid})" ) - assert str(upload_v1.hash) == ( - f"File SHA1: {upload_v1.hash.sha1} MD5 {upload_v1.hash.md5} " - f"SH256 {upload_v1.hash.sha256} Size {upload_v1.hash.size}B" + assert str(upload_v2.hash) == ( + f"File SHA1: {upload_v2.hash.sha1} MD5 {upload_v2.hash.md5} " + f"SH256 {upload_v2.hash.sha256} Size {upload_v2.hash.size}B" ) @@ -59,7 +72,7 @@ def test_get_upload_error(foss: Fossology, foss_server: str): upload_id = 100 responses.add( responses.GET, - f"{foss_server}/api/v2/uploads/{upload_id}", + f"{foss_server}/api/v1/uploads/{upload_id}", status=500, ) with pytest.raises(FossologyApiError) as excinfo: @@ -132,7 +145,7 @@ def test_empty_upload(foss: Fossology): def test_upload_error(foss: Fossology, foss_server: str, test_file_path: str): responses.add( responses.POST, - f"{foss_server}/api/v2/uploads", + f"{foss_server}/api/v1/uploads", status=500, ) description = "Test upload API error" @@ -172,7 +185,7 @@ def test_move_upload_error(foss: Fossology, foss_server: str, upload: Upload): folder = Folder(secrets.randbelow(1000), "Folder", "", foss.rootFolder) responses.add( responses.PUT, - f"{foss_server}/api/v2/uploads/{upload.id}", + f"{foss_server}/api/v1/uploads/{upload.id}", status=500, ) with pytest.raises(FossologyApiError): @@ -196,7 +209,7 @@ def test_update_upload_with_unknown_group_raises_error(foss: Fossology, upload: def test_update_upload_error(foss: Fossology, foss_server: str, upload: Upload): responses.add( responses.PATCH, - f"{foss_server}/api/v2/uploads/{upload.id}", + f"{foss_server}/api/v1/uploads/{upload.id}", status=500, ) with pytest.raises(FossologyApiError): @@ -217,7 +230,7 @@ def test_upload_summary(foss: Fossology, upload: Upload): def test_upload_summary_500_error(foss: Fossology, foss_server: str, upload: Upload): responses.add( responses.GET, - f"{foss_server}/api/v2/uploads/{upload.id}/summary", + f"{foss_server}/api/v1/uploads/{upload.id}/summary", status=500, ) with pytest.raises(FossologyApiError): @@ -284,7 +297,7 @@ def test_paginated_list_uploads(foss: Fossology, upload: Upload, test_file_path: def test_list_uploads_500_error(foss: Fossology, foss_server: str, upload: Upload): responses.add( responses.GET, - f"{foss_server}/api/v2/uploads", + f"{foss_server}/api/v1/uploads", status=500, ) with pytest.raises(FossologyApiError): @@ -310,7 +323,7 @@ def test_download_upload_authorization_error( ): responses.add( responses.GET, - f"{foss_server}/api/v2/uploads/{upload.id}/download", + f"{foss_server}/api/v1/uploads/{upload.id}/download", status=403, ) with pytest.raises(AuthorizationError) as excinfo: @@ -322,7 +335,7 @@ def test_download_upload_authorization_error( def test_download_upload_error(foss_server: str, foss: Fossology, upload: Upload): responses.add( responses.GET, - f"{foss_server}/api/v2/uploads/{upload.id}/download", + f"{foss_server}/api/v1/uploads/{upload.id}/download", status=401, ) with pytest.raises(FossologyApiError) as excinfo: