From acb07f79e4526a67225c027a3b7a9c099dcd1b49 Mon Sep 17 00:00:00 2001 From: jonathanlutterbeck <48634262+jonathanlutterbeck@users.noreply.github.com> Date: Fri, 15 Apr 2022 15:34:24 +0200 Subject: [PATCH 01/11] created configloader.py added a configuration loader which checks for existing config files. If the folder is not found it is created. If the file is not found the default empty config file is copied to users home directory. --- .idea/.gitignore | 3 + .idea/gridscale_api_client_python.iml | 15 ++++ .idea/inspectionProfiles/Project_Default.xml | 20 +++++ .../inspectionProfiles/profiles_settings.xml | 6 ++ .idea/misc.xml | 4 + .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 ++ examples/config.yaml | 9 +++ examples/configloader.py | 80 +++++++++++++++++++ examples/examples.py | 15 ++-- 10 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/gridscale_api_client_python.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 examples/config.yaml create mode 100644 examples/configloader.py diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/gridscale_api_client_python.iml b/.idea/gridscale_api_client_python.iml new file mode 100644 index 0000000..14273e4 --- /dev/null +++ b/.idea/gridscale_api_client_python.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..bc85450 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..5706053 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..52045b0 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/config.yaml b/examples/config.yaml new file mode 100644 index 0000000..9f7be7d --- /dev/null +++ b/examples/config.yaml @@ -0,0 +1,9 @@ +projects: +- name: default + userId: user123 + token: pass123 + url: https://api.gridscale.io +- name: something-else + userId: user456 + token: pass456 + url: https://api.gridscale.io \ No newline at end of file diff --git a/examples/configloader.py b/examples/configloader.py new file mode 100644 index 0000000..f8323d3 --- /dev/null +++ b/examples/configloader.py @@ -0,0 +1,80 @@ +import shutil +import yaml +import sys +import os.path + +#TODO: change active project +project = "somthing-else" + +def which_path(): + #check if os is linux + if(sys.platform == "linux" or sys.platform == "linux2"): + path = "~/.config/gridscale" + path = os.path.expanduser(path) + if not os.path.exists(path): + os.makedirs(path) + #check if os is windows + elif(sys.platform == "win32" or sys.platform == "cygwin" or sys.platform == "msys"): + path = "%APPDATA%\gridscale" + path = os.path.expanduser(path) + if not os.path.exists(path): + os.makedirs(path) + #check if os is mac os + elif(sys.platform == "darwin" or sys.platform == "os2" or sys.platform == "os2emx"): + path = "~/Library/Application Support/gridscale" + path = os.path.expanduser(path) + if not os.path.exists(path): + os.makedirs(path) + else: + "Operating System not supported" + + return path + +def create_config(path): + cwd = os.getcwd() + shutil.copyfile(f"{cwd}/config.yaml", path) + print(f"New config file created, edit config file at: {path}") + return + +def load_config(projectname, goal, path): + with open(f"{path}", 'r') as stream: + try: + data = yaml.safe_load(stream) + + for value in data.values(): + for x in range(len(value)): + result = value[x] + #returns userID and token for the selected project + if (result.get("name") == projectname): + userid = result.get("userId") + token = result.get("token") + except yaml.YAMLError as exc: + print(exc) + + if(goal == "id"): + return userid + elif(goal == "token"): + return token + +def load_token(project): + syspath = which_path() + "/config.yaml" + goal = "token" + + # check if config file exists + if not os.path.exists(syspath): + create_config(syspath) + return load_config(project, goal, syspath) + else: + return load_config(project, goal, syspath) + + +def load_userid(project): + syspath = which_path() + "/config.yaml" + goal = "id" + + # check if config file exists + if not os.path.exists(syspath): + create_config(syspath) + return load_config(project, goal, syspath) + else: + return load_config(project, goal, syspath) diff --git a/examples/examples.py b/examples/examples.py index 2136e38..ee20fa8 100644 --- a/examples/examples.py +++ b/examples/examples.py @@ -7,6 +7,8 @@ from gs_api_client import SyncGridscaleApiClient, GridscaleApiClient, models from gs_api_client import Configuration +from configloader import load_token, load_userid + if __name__ == '__main__': @@ -16,11 +18,12 @@ # api_config.debug = True api_config.host = 'https://api.gridscale.io' - #TODO: Insert your API token and User ID - api_config.api_key['X-Auth-Token'] = "AUTH_TOKEN" - api_config.api_key['X-Auth-UserId'] = "USER_UUID" - # api_config.debug = True - + #TODO: Change project + project = "default" + api_config.api_key['X-Auth-Token'] = load_token(project) + api_config.api_key['X-Auth-UserId'] = load_userid(project) + api_config.debug = True + print('-' * 80) client = SyncGridscaleApiClient(configuration=api_config, http_info=False) @@ -34,7 +37,7 @@ get_templates_response = client.get_templates() templates = get_templates_response['templates'].values() template_by_name = index_by_key(templates, 'name') - template = template_by_name['Debian 9'] + template = template_by_name['Debian 11'] # create storage create_storage_response = client.create_storage({ From 09ef4af0001ce24eabaf44f0dc47de10df9622f5 Mon Sep 17 00:00:00 2001 From: jonathanlutterbeck <48634262+jonathanlutterbeck@users.noreply.github.com> Date: Fri, 15 Apr 2022 20:34:34 +0200 Subject: [PATCH 02/11] improved code added suggestions from pylint --- examples/configloader.py | 9 ++++----- examples/examples.py | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/configloader.py b/examples/configloader.py index f8323d3..75bccce 100644 --- a/examples/configloader.py +++ b/examples/configloader.py @@ -1,26 +1,26 @@ import shutil -import yaml import sys import os.path +import yaml #TODO: change active project project = "somthing-else" def which_path(): #check if os is linux - if(sys.platform == "linux" or sys.platform == "linux2"): + if(sys.platform in ("linux", "linux2")): path = "~/.config/gridscale" path = os.path.expanduser(path) if not os.path.exists(path): os.makedirs(path) #check if os is windows - elif(sys.platform == "win32" or sys.platform == "cygwin" or sys.platform == "msys"): + elif(sys.platform in ("win32", "cygwin", "msys")): path = "%APPDATA%\gridscale" path = os.path.expanduser(path) if not os.path.exists(path): os.makedirs(path) #check if os is mac os - elif(sys.platform == "darwin" or sys.platform == "os2" or sys.platform == "os2emx"): + elif(sys.platform in ("darwin", "os2", "os2emx")): path = "~/Library/Application Support/gridscale" path = os.path.expanduser(path) if not os.path.exists(path): @@ -34,7 +34,6 @@ def create_config(path): cwd = os.getcwd() shutil.copyfile(f"{cwd}/config.yaml", path) print(f"New config file created, edit config file at: {path}") - return def load_config(projectname, goal, path): with open(f"{path}", 'r') as stream: diff --git a/examples/examples.py b/examples/examples.py index ee20fa8..9a1a572 100644 --- a/examples/examples.py +++ b/examples/examples.py @@ -3,11 +3,11 @@ from pprint import pprint from uuid import uuid4 from index_by.key import index_by_key +from configloader import load_token, load_userid from gs_api_client import SyncGridscaleApiClient, GridscaleApiClient, models from gs_api_client import Configuration -from configloader import load_token, load_userid if __name__ == '__main__': From 2c620621beae4eaa65a77500c68198e17bda6b53 Mon Sep 17 00:00:00 2001 From: Benjamin Kircher Date: Fri, 22 Apr 2022 11:12:10 +0200 Subject: [PATCH 03/11] Add test case for load_config Add a test case that loads a simple YAML file with two projects. You can run tests by executing `pytest` in the project root. Note that the test currently fails. --- examples/__init__.py | 0 tests/example-config.yaml | 10 ++++++++++ tests/test_config.py | 16 ++++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 examples/__init__.py create mode 100644 tests/example-config.yaml diff --git a/examples/__init__.py b/examples/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/example-config.yaml b/tests/example-config.yaml new file mode 100644 index 0000000..adff316 --- /dev/null +++ b/tests/example-config.yaml @@ -0,0 +1,10 @@ +projects: + - name: default + userId: user123 + token: pass123 + url: https://api.gridscale.io + + - name: something-else + userId: user456 + token: pass456 + url: https://api.gridscale.io diff --git a/tests/test_config.py b/tests/test_config.py index d5ceaab..3e288b9 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,5 +1,10 @@ +import os.path + from gs_api_client import Configuration +from examples.configloader import load_config + +CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) def test_debug_is_disabled_by_default(): config = Configuration() @@ -9,3 +14,14 @@ def test_debug_is_disabled_by_default(): def test_tls_certs_are_verified_by_default(): config = Configuration() assert config.verify_ssl + + +def test_load_config_from_yaml(): + """"Make sure we can load a config from a given YAML file.""" + + example_config = os.path.join(CURRENT_DIR, "example-config.yaml") + res = load_config(example_config) + assert isinstance(res, list) + assert len(res) == 2 + assert res[0]["name"] == "default" + assert res[1]["name"] == "something-else" From 05c3775568c142f17aabc48e1407f86be8a14ea2 Mon Sep 17 00:00:00 2001 From: jonathanlutterbeck <48634262+jonathanlutterbeck@users.noreply.github.com> Date: Fri, 22 Apr 2022 11:27:59 +0200 Subject: [PATCH 04/11] Update dev-requirements.txt added yaml import --- dev-requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/dev-requirements.txt b/dev-requirements.txt index d8b1b8f..ebc6e29 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -7,3 +7,4 @@ rope black pytest pytest-mock +yaml From 415112c2d30c7407f1aaa4708ed28af2fa4b73a5 Mon Sep 17 00:00:00 2001 From: Jonathan Lutterbeck Date: Mon, 25 Apr 2022 19:15:34 +0200 Subject: [PATCH 05/11] Improve code to run checks The tests now are green. I just changed it to return the contents of the yaml file. In the test no path has to be passed. This is already done in the configloader. Can I remove this from the test @bkircher ? --- examples/configloader.py | 47 +++++++++------------------------------- examples/examples.py | 11 ++++++---- 2 files changed, 17 insertions(+), 41 deletions(-) diff --git a/examples/configloader.py b/examples/configloader.py index 75bccce..fc8d98f 100644 --- a/examples/configloader.py +++ b/examples/configloader.py @@ -26,7 +26,7 @@ def which_path(): if not os.path.exists(path): os.makedirs(path) else: - "Operating System not supported" + print("Operating System not supported") return path @@ -35,45 +35,18 @@ def create_config(path): shutil.copyfile(f"{cwd}/config.yaml", path) print(f"New config file created, edit config file at: {path}") -def load_config(projectname, goal, path): - with open(f"{path}", 'r') as stream: - try: - data = yaml.safe_load(stream) - - for value in data.values(): - for x in range(len(value)): - result = value[x] - #returns userID and token for the selected project - if (result.get("name") == projectname): - userid = result.get("userId") - token = result.get("token") - except yaml.YAMLError as exc: - print(exc) - - if(goal == "id"): - return userid - elif(goal == "token"): - return token - -def load_token(project): +def load_config(path): syspath = which_path() + "/config.yaml" - goal = "token" - # check if config file exists if not os.path.exists(syspath): create_config(syspath) - return load_config(project, goal, syspath) - else: - return load_config(project, goal, syspath) - -def load_userid(project): - syspath = which_path() + "/config.yaml" - goal = "id" + with open(f"{syspath}", 'r') as stream: + try: + data = yaml.safe_load(stream) + #return list of dictionaries for all projects + for value in data.values(): + return(value) - # check if config file exists - if not os.path.exists(syspath): - create_config(syspath) - return load_config(project, goal, syspath) - else: - return load_config(project, goal, syspath) + except yaml.YAMLError as exc: + print(exc) diff --git a/examples/examples.py b/examples/examples.py index 9a1a572..c55ee06 100644 --- a/examples/examples.py +++ b/examples/examples.py @@ -2,13 +2,15 @@ from pprint import pprint from uuid import uuid4 +import os from index_by.key import index_by_key -from configloader import load_token, load_userid +from configloader import load_config from gs_api_client import SyncGridscaleApiClient, GridscaleApiClient, models from gs_api_client import Configuration +CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) if __name__ == '__main__': @@ -19,9 +21,10 @@ api_config.host = 'https://api.gridscale.io' #TODO: Change project - project = "default" - api_config.api_key['X-Auth-Token'] = load_token(project) - api_config.api_key['X-Auth-UserId'] = load_userid(project) + example_config = os.path.join(CURRENT_DIR, "config.yaml") + configfile = load_config(example_config) + api_config.api_key['X-Auth-Token'] = configfile[0].get("token") + api_config.api_key['X-Auth-UserId'] = configfile[0].get("userId") api_config.debug = True print('-' * 80) From 6aed0110df2dc3e4ea5ca413d745e878ba1c743b Mon Sep 17 00:00:00 2001 From: jonathanlutterbeck <48634262+jonathanlutterbeck@users.noreply.github.com> Date: Mon, 25 Apr 2022 19:39:26 +0200 Subject: [PATCH 06/11] Changed yaml to pyyaml Added misssing dependency pyyaml for pytest --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index ebc6e29..2a221be 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -7,4 +7,4 @@ rope black pytest pytest-mock -yaml +pyyaml From d7d1915fbfedfd157ef424967d8402a602931de6 Mon Sep 17 00:00:00 2001 From: Jonathan Lutterbeck Date: Mon, 25 Apr 2022 19:41:45 +0200 Subject: [PATCH 07/11] Fixed error The function was copying the file to the wrong location --- examples/configloader.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/configloader.py b/examples/configloader.py index fc8d98f..84c6522 100644 --- a/examples/configloader.py +++ b/examples/configloader.py @@ -32,8 +32,8 @@ def which_path(): def create_config(path): cwd = os.getcwd() - shutil.copyfile(f"{cwd}/config.yaml", path) - print(f"New config file created, edit config file at: {path}") + shutil.copyfile(f"{cwd}/config.yaml", syspath) + print(f"New config file created, edit config file at: {syspath}") def load_config(path): syspath = which_path() + "/config.yaml" From a65854313d337bd2b404cc0a0046e5d1ff26fe45 Mon Sep 17 00:00:00 2001 From: Benjamin Kircher Date: Wed, 27 Apr 2022 09:18:58 +0200 Subject: [PATCH 08/11] tests: Add more test cases for load_config() --- tests/test_config.py | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/tests/test_config.py b/tests/test_config.py index 3e288b9..d6149b1 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,11 +1,14 @@ import os.path +import pytest + from gs_api_client import Configuration from examples.configloader import load_config CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) + def test_debug_is_disabled_by_default(): config = Configuration() assert not config.debug @@ -17,11 +20,45 @@ def test_tls_certs_are_verified_by_default(): def test_load_config_from_yaml(): - """"Make sure we can load a config from a given YAML file.""" + """Make sure we can load a config from a given YAML file.""" example_config = os.path.join(CURRENT_DIR, "example-config.yaml") res = load_config(example_config) assert isinstance(res, list) assert len(res) == 2 + assert isinstance(res[0], dict) assert res[0]["name"] == "default" assert res[1]["name"] == "something-else" + + +def test_load_config_handles_non_existing_file(): + """ "Ensure load_config raises FileNotFoundError.""" + + with pytest.raises(FileNotFoundError): + load_config("fufu.yaml") + + +def test_load_config_checks_for_bogus_input(): + """ "Ensure load_config checks it's input.""" + + with pytest.raises(AssertionError): + load_config(42) + + with pytest.raises(AssertionError): + load_config("") + + +def test_load_config_propagates_parsing_errors(): + """ "Ensure load_config raises any error during parsing.""" + + import yaml + + not_a_yaml_file = os.path.join(CURRENT_DIR, "test_config.py") + with pytest.raises(yaml.YAMLError): + load_config(not_a_yaml_file) + + +def test_load_config_has_doc_string(): + """ "Make sure load_config is documented.""" + + assert load_config.__doc__ From 5fe3df0963e5c79d1e3b525dbe53fe4536c2d9a5 Mon Sep 17 00:00:00 2001 From: Jonathan Lutterbeck Date: Mon, 25 Apr 2022 19:45:13 +0200 Subject: [PATCH 09/11] improvements to check validity of config file Checks have been added to verify file name lenght and to check if it's really a .yaml file. All tests are now green. Fixed error Moved the syspath cariable to be globally available Fix error path variable was named wrong Try to fix github action error Changed file name for config file. Fixed Test Fixed copyfile for test case Check if config is valid load_config now gives correct error messages and does not catch any exceptions. The functions are now also properly seperated. removed print statements Print statements have been removed and replaced them with RuntimeErrors where applicable. which_path was renamed to default_config_path. Improved code I merged the two isInstance calls to improve readability. --- examples/configloader.py | 48 ++++++++++++++++++++++++---------------- examples/examples.py | 7 ++---- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/examples/configloader.py b/examples/configloader.py index 84c6522..4f01d3f 100644 --- a/examples/configloader.py +++ b/examples/configloader.py @@ -1,12 +1,17 @@ import shutil import sys import os.path +import pathlib import yaml -#TODO: change active project -project = "somthing-else" -def which_path(): +def default_config_path(): + """ + + this checks the operation system of the user. + this is used to determine the standard save location for the global gridscale config file. + + """ #check if os is linux if(sys.platform in ("linux", "linux2")): path = "~/.config/gridscale" @@ -26,27 +31,32 @@ def which_path(): if not os.path.exists(path): os.makedirs(path) else: - print("Operating System not supported") + raise RuntimeError("Operating system not supported") return path -def create_config(path): - cwd = os.getcwd() - shutil.copyfile(f"{cwd}/config.yaml", syspath) - print(f"New config file created, edit config file at: {syspath}") -def load_config(path): - syspath = which_path() + "/config.yaml" +def create_config(path): + """ + this will copy the currently used config file in the standard folder + """ + syspath = default_config_path() + "/config.yaml" + shutil.copyfile(path, syspath) - if not os.path.exists(syspath): - create_config(syspath) - with open(f"{syspath}", 'r') as stream: - try: +def load_config(path): + """ + First checking "path" to match minimum length and other requirements. + + Then it opens the specified config file and returns all keys which include token and UserId. + """ + # opens specified file to retrieve config tokens + if isinstance(path, (pathlib.Path, str)): + assert path + with open(f"{path}", 'r') as stream: data = yaml.safe_load(stream) - #return list of dictionaries for all projects + # return list of dictionaries for all projects for value in data.values(): - return(value) - - except yaml.YAMLError as exc: - print(exc) + return (value) + else: + raise AssertionError diff --git a/examples/examples.py b/examples/examples.py index c55ee06..2dc1f03 100644 --- a/examples/examples.py +++ b/examples/examples.py @@ -10,8 +10,6 @@ from gs_api_client import Configuration -CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) - if __name__ == '__main__': # run `pip3 install index_by` before executing this file @@ -20,9 +18,8 @@ # api_config.debug = True api_config.host = 'https://api.gridscale.io' - #TODO: Change project - example_config = os.path.join(CURRENT_DIR, "config.yaml") - configfile = load_config(example_config) + #TODO: Change filename + configfile = load_config("config.yaml") api_config.api_key['X-Auth-Token'] = configfile[0].get("token") api_config.api_key['X-Auth-UserId'] = configfile[0].get("userId") api_config.debug = True From ebc5df8c4bd4c17bc0763badcef35ce208d5303e Mon Sep 17 00:00:00 2001 From: Benjamin Kircher Date: Wed, 4 May 2022 10:05:50 +0200 Subject: [PATCH 10/11] tests: Ensure load_config does not interpret file name --- tests/test_config.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_config.py b/tests/test_config.py index d6149b1..d267bee 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,4 +1,5 @@ import os.path +import shutil import pytest @@ -31,6 +32,17 @@ def test_load_config_from_yaml(): assert res[1]["name"] == "something-else" +def test_load_config_works_without_fileext(tmp_path): + """Ensure load_config does not interpret file path or file name.""" + + example_config = os.path.join(CURRENT_DIR, "example-config.yaml") + dest = tmp_path / "a" + shutil.copyfile(example_config, dest) + res = load_config(dest) + assert isinstance(res, list) + assert len(res) == 2 + + def test_load_config_handles_non_existing_file(): """ "Ensure load_config raises FileNotFoundError.""" From cf0f7396a5ce7e62e574da0e2d4396df99a152c1 Mon Sep 17 00:00:00 2001 From: "Jonathan, Lutterbeck" Date: Thu, 2 Jun 2022 18:15:08 +0200 Subject: [PATCH 11/11] Squashed commit of the following: commit ebc5df8c4bd4c17bc0763badcef35ce208d5303e Author: Benjamin Kircher Date: Wed May 4 10:05:50 2022 +0200 tests: Ensure load_config does not interpret file name commit 5fe3df0963e5c79d1e3b525dbe53fe4536c2d9a5 Author: Jonathan Lutterbeck Date: Mon Apr 25 19:45:13 2022 +0200 improvements to check validity of config file Checks have been added to verify file name lenght and to check if it's really a .yaml file. All tests are now green. Fixed error Moved the syspath cariable to be globally available Fix error path variable was named wrong Try to fix github action error Changed file name for config file. Fixed Test Fixed copyfile for test case Check if config is valid load_config now gives correct error messages and does not catch any exceptions. The functions are now also properly seperated. removed print statements Print statements have been removed and replaced them with RuntimeErrors where applicable. which_path was renamed to default_config_path. Improved code I merged the two isInstance calls to improve readability. commit a65854313d337bd2b404cc0a0046e5d1ff26fe45 Author: Benjamin Kircher Date: Wed Apr 27 09:18:58 2022 +0200 tests: Add more test cases for load_config() commit 3566688a3c8b090437c52523d8f9fe08365a8f54 Merge: d7d1915 6aed011 Author: Jonathan Lutterbeck Date: Mon Apr 25 19:42:41 2022 +0200 Merge branch 'enhancement/20-allow-reading-and-writing-shared-config-file' of https://github.com/gridscale/gridscale_api_client_python into enhancement/20-allow-reading-and-writing-shared-config-file commit d7d1915fbfedfd157ef424967d8402a602931de6 Author: Jonathan Lutterbeck Date: Mon Apr 25 19:41:45 2022 +0200 Fixed error The function was copying the file to the wrong location commit 6aed0110df2dc3e4ea5ca413d745e878ba1c743b Author: jonathanlutterbeck <48634262+jonathanlutterbeck@users.noreply.github.com> Date: Mon Apr 25 19:39:26 2022 +0200 Changed yaml to pyyaml Added misssing dependency pyyaml for pytest commit 415112c2d30c7407f1aaa4708ed28af2fa4b73a5 Author: Jonathan Lutterbeck Date: Mon Apr 25 19:15:34 2022 +0200 Improve code to run checks The tests now are green. I just changed it to return the contents of the yaml file. In the test no path has to be passed. This is already done in the configloader. Can I remove this from the test @bkircher ? commit 05c3775568c142f17aabc48e1407f86be8a14ea2 Author: jonathanlutterbeck <48634262+jonathanlutterbeck@users.noreply.github.com> Date: Fri Apr 22 11:27:59 2022 +0200 Update dev-requirements.txt added yaml import commit 2c620621beae4eaa65a77500c68198e17bda6b53 Author: Benjamin Kircher Date: Fri Apr 22 11:12:10 2022 +0200 Add test case for load_config Add a test case that loads a simple YAML file with two projects. You can run tests by executing `pytest` in the project root. Note that the test currently fails. commit 09ef4af0001ce24eabaf44f0dc47de10df9622f5 Author: jonathanlutterbeck <48634262+jonathanlutterbeck@users.noreply.github.com> Date: Fri Apr 15 20:34:34 2022 +0200 improved code added suggestions from pylint commit acb07f79e4526a67225c027a3b7a9c099dcd1b49 Author: jonathanlutterbeck <48634262+jonathanlutterbeck@users.noreply.github.com> Date: Fri Apr 15 15:34:24 2022 +0200 created configloader.py added a configuration loader which checks for existing config files. If the folder is not found it is created. If the file is not found the default empty config file is copied to users home directory. --- .idea/.gitignore | 3 + .idea/gridscale_api_client_python.iml | 15 +++++ .idea/inspectionProfiles/Project_Default.xml | 20 ++++++ .../inspectionProfiles/profiles_settings.xml | 6 ++ .idea/misc.xml | 4 ++ .idea/modules.xml | 8 +++ .idea/vcs.xml | 6 ++ dev-requirements.txt | 1 + examples/__init__.py | 0 examples/config.yaml | 9 +++ examples/configloader.py | 62 ++++++++++++++++++ examples/examples.py | 13 ++-- tests/example-config.yaml | 10 +++ tests/test_config.py | 65 +++++++++++++++++++ 14 files changed, 217 insertions(+), 5 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/gridscale_api_client_python.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 examples/__init__.py create mode 100644 examples/config.yaml create mode 100644 examples/configloader.py create mode 100644 tests/example-config.yaml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/gridscale_api_client_python.iml b/.idea/gridscale_api_client_python.iml new file mode 100644 index 0000000..14273e4 --- /dev/null +++ b/.idea/gridscale_api_client_python.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..bc85450 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..5706053 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..52045b0 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/dev-requirements.txt b/dev-requirements.txt index d8b1b8f..2a221be 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -7,3 +7,4 @@ rope black pytest pytest-mock +pyyaml diff --git a/examples/__init__.py b/examples/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/config.yaml b/examples/config.yaml new file mode 100644 index 0000000..9f7be7d --- /dev/null +++ b/examples/config.yaml @@ -0,0 +1,9 @@ +projects: +- name: default + userId: user123 + token: pass123 + url: https://api.gridscale.io +- name: something-else + userId: user456 + token: pass456 + url: https://api.gridscale.io \ No newline at end of file diff --git a/examples/configloader.py b/examples/configloader.py new file mode 100644 index 0000000..4f01d3f --- /dev/null +++ b/examples/configloader.py @@ -0,0 +1,62 @@ +import shutil +import sys +import os.path +import pathlib +import yaml + + +def default_config_path(): + """ + + this checks the operation system of the user. + this is used to determine the standard save location for the global gridscale config file. + + """ + #check if os is linux + if(sys.platform in ("linux", "linux2")): + path = "~/.config/gridscale" + path = os.path.expanduser(path) + if not os.path.exists(path): + os.makedirs(path) + #check if os is windows + elif(sys.platform in ("win32", "cygwin", "msys")): + path = "%APPDATA%\gridscale" + path = os.path.expanduser(path) + if not os.path.exists(path): + os.makedirs(path) + #check if os is mac os + elif(sys.platform in ("darwin", "os2", "os2emx")): + path = "~/Library/Application Support/gridscale" + path = os.path.expanduser(path) + if not os.path.exists(path): + os.makedirs(path) + else: + raise RuntimeError("Operating system not supported") + + return path + + +def create_config(path): + """ + this will copy the currently used config file in the standard folder + """ + syspath = default_config_path() + "/config.yaml" + shutil.copyfile(path, syspath) + + +def load_config(path): + """ + First checking "path" to match minimum length and other requirements. + + Then it opens the specified config file and returns all keys which include token and UserId. + """ + # opens specified file to retrieve config tokens + if isinstance(path, (pathlib.Path, str)): + assert path + with open(f"{path}", 'r') as stream: + data = yaml.safe_load(stream) + # return list of dictionaries for all projects + for value in data.values(): + return (value) + else: + raise AssertionError diff --git a/examples/examples.py b/examples/examples.py index e29f7c8..2dc1f03 100644 --- a/examples/examples.py +++ b/examples/examples.py @@ -2,7 +2,9 @@ from pprint import pprint from uuid import uuid4 +import os from index_by.key import index_by_key +from configloader import load_config from gs_api_client import SyncGridscaleApiClient, GridscaleApiClient, models from gs_api_client import Configuration @@ -16,11 +18,12 @@ # api_config.debug = True api_config.host = 'https://api.gridscale.io' - #TODO: Insert your API token and User ID - api_config.api_key['X-Auth-Token'] = "AUTH_TOKEN" - api_config.api_key['X-Auth-UserId'] = "USER_UUID" - # api_config.debug = True - + #TODO: Change filename + configfile = load_config("config.yaml") + api_config.api_key['X-Auth-Token'] = configfile[0].get("token") + api_config.api_key['X-Auth-UserId'] = configfile[0].get("userId") + api_config.debug = True + print('-' * 80) client = SyncGridscaleApiClient(configuration=api_config, http_info=False) diff --git a/tests/example-config.yaml b/tests/example-config.yaml new file mode 100644 index 0000000..adff316 --- /dev/null +++ b/tests/example-config.yaml @@ -0,0 +1,10 @@ +projects: + - name: default + userId: user123 + token: pass123 + url: https://api.gridscale.io + + - name: something-else + userId: user456 + token: pass456 + url: https://api.gridscale.io diff --git a/tests/test_config.py b/tests/test_config.py index d5ceaab..d267bee 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,4 +1,13 @@ +import os.path +import shutil + +import pytest + from gs_api_client import Configuration +from examples.configloader import load_config + + +CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) def test_debug_is_disabled_by_default(): @@ -9,3 +18,59 @@ def test_debug_is_disabled_by_default(): def test_tls_certs_are_verified_by_default(): config = Configuration() assert config.verify_ssl + + +def test_load_config_from_yaml(): + """Make sure we can load a config from a given YAML file.""" + + example_config = os.path.join(CURRENT_DIR, "example-config.yaml") + res = load_config(example_config) + assert isinstance(res, list) + assert len(res) == 2 + assert isinstance(res[0], dict) + assert res[0]["name"] == "default" + assert res[1]["name"] == "something-else" + + +def test_load_config_works_without_fileext(tmp_path): + """Ensure load_config does not interpret file path or file name.""" + + example_config = os.path.join(CURRENT_DIR, "example-config.yaml") + dest = tmp_path / "a" + shutil.copyfile(example_config, dest) + res = load_config(dest) + assert isinstance(res, list) + assert len(res) == 2 + + +def test_load_config_handles_non_existing_file(): + """ "Ensure load_config raises FileNotFoundError.""" + + with pytest.raises(FileNotFoundError): + load_config("fufu.yaml") + + +def test_load_config_checks_for_bogus_input(): + """ "Ensure load_config checks it's input.""" + + with pytest.raises(AssertionError): + load_config(42) + + with pytest.raises(AssertionError): + load_config("") + + +def test_load_config_propagates_parsing_errors(): + """ "Ensure load_config raises any error during parsing.""" + + import yaml + + not_a_yaml_file = os.path.join(CURRENT_DIR, "test_config.py") + with pytest.raises(yaml.YAMLError): + load_config(not_a_yaml_file) + + +def test_load_config_has_doc_string(): + """ "Make sure load_config is documented.""" + + assert load_config.__doc__