From 468ff1aec3899aa002165ecb9bfa817934e7639f Mon Sep 17 00:00:00 2001 From: zeryx <1892175+zeryx@users.noreply.github.com> Date: Wed, 11 May 2022 15:26:52 -0300 Subject: [PATCH 01/10] major updates to all paths, removal of class creation from API requests --- Algorithmia/CLI.py | 12 +- Algorithmia/algorithm.py | 107 ++++----- Algorithmia/client.py | 20 +- Algorithmia/util.py | 5 + Test/api/app.py | 114 +++++---- Test/api/self_signed_app.py | 23 ++ Test/regular/algo_test.py | 65 ++--- Test/regular/client_test.py | 91 +------ Test/self_signed/acl_test.py | 38 --- Test/self_signed/algo_test.py | 68 +----- Test/self_signed/client_test.py | 413 -------------------------------- 11 files changed, 213 insertions(+), 743 deletions(-) delete mode 100644 Test/self_signed/acl_test.py delete mode 100644 Test/self_signed/client_test.py diff --git a/Algorithmia/CLI.py b/Algorithmia/CLI.py index 3acc6ae..e0f7bab 100644 --- a/Algorithmia/CLI.py +++ b/Algorithmia/CLI.py @@ -1,11 +1,10 @@ import Algorithmia import os -from Algorithmia.errors import DataApiError +from Algorithmia.errors import DataApiError, AlgorithmException from Algorithmia.algo_response import AlgoResponse import json, re, requests, six import toml import shutil -from time import time class CLI: def __init__(self): @@ -309,11 +308,12 @@ def list_languages(self, client): return table def getBuildLogs(self, user, algo, client): - api_response = client.algo(user + '/' + algo).build_logs() + try: + api_response = client.algo(user + '/' + algo).builds().json() + return json.dumps(api_response['results'], indent=1) + except AlgorithmException as e: + return json.dumps(e) - if "error" in api_response: - return json.dumps(api_response) - return json.dumps(api_response['results'], indent=1) def getconfigfile(self): if (os.name == "posix"): diff --git a/Algorithmia/algorithm.py b/Algorithmia/algorithm.py index 378e1c0..f7fd6b5 100644 --- a/Algorithmia/algorithm.py +++ b/Algorithmia/algorithm.py @@ -1,11 +1,10 @@ 'Algorithmia Algorithm API Client (python)' -import base64 import json import re from Algorithmia.async_response import AsyncResponse from Algorithmia.algo_response import AlgoResponse -from Algorithmia.errors import ApiError, ApiInternalError, raiseAlgoApiError +from Algorithmia.errors import ApiError, ApiInternalError, raiseAlgoApiError, AlgorithmException from enum import Enum from algorithmia_api_client.rest import ApiException from algorithmia_api_client import CreateRequest, UpdateRequest, VersionRequest, Details, Settings, SettingsMandatory, \ @@ -73,72 +72,60 @@ def update(self, details={}, settings={}, version_info={}): # Publish an algorithm def publish(self, details={}, settings={}, version_info={}): - publish_parameters = {"details": details, "settings": settings, "version_info": version_info} - url = "/v1/algorithms/" + self.username + "/" + self.algoname + "/versions" - print(publish_parameters) - api_response = self.client.postJsonHelper(url, publish_parameters, parse_response_as_json=True, - **self.query_parameters) - return api_response - # except ApiException as e: - # error_message = json.loads(e.body) - # raise raiseAlgoApiError(error_message) - - def builds(self, limit=56, marker=None): try: - if marker is not None: - api_response = self.client.manageApi.get_algorithm_builds(self.username, self.algoname, limit=limit, - marker=marker) - else: - api_response = self.client.manageApi.get_algorithm_builds(self.username, self.algoname, limit=limit) + publish_parameters = {"details": details, "settings": settings, "version_info": version_info} + url = "/v1/algorithms/" + self.username + "/" + self.algoname + "/versions" + print(publish_parameters) + api_response = self.client.postJsonHelper(url, publish_parameters, parse_response_as_json=True, + **self.query_parameters) return api_response except ApiException as e: error_message = json.loads(e.body) raise raiseAlgoApiError(error_message) + def builds(self, limit=56, marker=None): + kwargs = {"limit": limit, "marker": marker} + url = "/v1/algorithms/" + self.username + "/" + self.algoname + '/builds' + response = self.client.getHelper(url, **kwargs) + return response + def get_build(self, build_id): # Get the build object for a given build_id # The build status can have one of the following value: succeeded, failed, in-progress - try: - api_response = self.client.manageApi.get_algorithm_build_by_id(self.username, self.algoname, build_id) - return api_response - except ApiException as e: - error_message = json.loads(e.body) - raise raiseAlgoApiError(error_message) + url = '/v1/algorithms/' + self.username + '/' + self.algoname + '/builds/' + build_id + response = self.client.getHelperAsJson(url) + return response def get_build_logs(self, build_id): # Get the algorithm build logs for a given build_id - try: - api_response = self.client.manageApi.get_algorithm_build_logs(self.username, self.algoname, build_id) - return api_response - except ApiException as e: - error_message = json.loads(e.body) - raise raiseAlgoApiError(error_message) - - def build_logs(self): - url = '/v1/algorithms/' + self.username + '/' + self.algoname + '/builds' - response = json.loads(self.client.getHelper(url).content.decode('utf-8')) + url = '/v1/algorithms/' + self.username + '/' + self.algoname + '/builds/' + build_id + '/logs' + response = self.client.getHelperAsJson(url) return response def get_scm_status(self): - try: - api_response = self.client.manageApi.get_algorithm_scm_connection_status(self.username, self.algoname) - return api_response - except ApiException as e: - error_message = json.loads(e.body) - raise raiseAlgoApiError(error_message) + url = '/v1/algorithms/' + self.username + '/' + self.algoname + '/scm/status' + response = self.client.getHelperAsJson(url) + return response # Get info on an algorithm def info(self, algo_hash=None): + # Get Algorithm + if algo_hash: + url = '/v1/algorithms/' + self.username + '/' + self.algoname + '/versions/' + algo_hash + else: + url = '/v1/algorithms/' + self.username + '/' + self.algoname + '/versions' + response = self.client.getHelperAsJson(url) + return response + + # Check if an Algorithm exists + def exists(self): try: - # Get Algorithm - if algo_hash: - api_response = self.client.manageApi.get_algorithm_hash_version(self.username, self.algoname, algo_hash) - else: - api_response = self.client.manageApi.get_algorithm(self.username, self.algoname) - return api_response - except ApiException as e: - error_message = json.loads(e.body) - raise raiseAlgoApiError(error_message) + url = '/v1/algorithms/' + self.username + '/' + self.algoname + _ = self.client.getHelperAsJson(url) + return True + except AlgorithmException as e: + print(e) + return False # Get all versions of the algorithm, with the given filters def versions(self, limit=None, marker=None, published=None, callable=None): @@ -154,23 +141,17 @@ def versions(self, limit=None, marker=None, published=None, callable=None): if callable: c = callable kwargs["callable"] = str(c).lower() if str(c) in bools else c - try: - # Get Algorithm versions - api_response = self.client.manageApi.get_algorithm_versions(self.username, self.algoname, **kwargs) - return api_response - except ApiException as e: - error_message = json.loads(e.body) - raise raiseAlgoApiError(error_message) + # Get Algorithm versions + url = '/v1/algorithms/' + self.username + '/' + self.algoname + '/versions' + response = self.client.getHelperAsJson(url) + return response # Compile an algorithm def compile(self): - try: - # Compile algorithm - api_response = self.client.manageApi.compile_algorithm(self.username, self.algoname) - return api_response - except ApiException as e: - error_message = json.loads(e.body) - raise raiseAlgoApiError(error_message) + # Compile algorithm + url = '/v1/algorithms/' + self.username + '/' + self.algoname + '/compile' + response = self.client.postJsonHelper(url, {}, parse_response_as_json=True) + return response # Pipe an input into this algorithm def pipe(self, input1): diff --git a/Algorithmia/client.py b/Algorithmia/client.py index e72a8f6..97fd24e 100644 --- a/Algorithmia/client.py +++ b/Algorithmia/client.py @@ -2,11 +2,12 @@ import Algorithmia from Algorithmia.insights import Insights +from Algorithmia.errors import raiseAlgoApiError from Algorithmia.algorithm import Algorithm from Algorithmia.datafile import DataFile, LocalDataFile, AdvancedDataFile from Algorithmia.datadirectory import DataDirectory, LocalDataDirectory, AdvancedDataDirectory from algorithmia_api_client import Configuration, DefaultApi, ApiClient -from Algorithmia.util import md5_for_file, md5_for_str +from Algorithmia.util import md5_for_file, md5_for_str, ResponseWrapper from tempfile import mkstemp import atexit import json, re, requests, six, certifi @@ -15,6 +16,7 @@ from time import time + class Client(object): 'Algorithmia Common Library' @@ -257,6 +259,22 @@ def getHelper(self, url, **query_parameters): headers['Authorization'] = 'Bearer ' + self.bearerToken return self.requestSession.get(self.apiAddress + url, headers=headers, params=query_parameters) + def getHelperAsJson(self, url, **query_parameters): + headers = {} + if self.apiKey is not None: + headers['Authorization'] = self.apiKey + elif self.bearerToken is not None: + headers['Authorization'] = 'Bearer ' + self.bearerToken + response = self.requestSession.get(self.apiAddress + url, headers=headers, params=query_parameters) + if response.status_code == 200: + response = response.json() + if 'error' in response: + raise raiseAlgoApiError(response) + else: + return response + else: + raise raiseAlgoApiError(response) + def getStreamHelper(self, url, **query_parameters): headers = {} if self.apiKey is not None: diff --git a/Algorithmia/util.py b/Algorithmia/util.py index 92aa3b3..27cf236 100644 --- a/Algorithmia/util.py +++ b/Algorithmia/util.py @@ -47,3 +47,8 @@ def md5_for_str(content): hash_md5 = hashlib.md5() hash_md5.update(content.encode()) return str(hash_md5.hexdigest()) + + +class ResponseWrapper: + def __init__(self, **entries): + self.__dict__.update(entries) \ No newline at end of file diff --git a/Test/api/app.py b/Test/api/app.py index 99c192b..19c4b6a 100644 --- a/Test/api/app.py +++ b/Test/api/app.py @@ -6,19 +6,18 @@ from multiprocessing import Process import uvicorn - - regular_app = FastAPI() def start_webserver_reg(): def _start_webserver(): uvicorn.run(regular_app, host="127.0.0.1", port=8080, log_level="debug") - + p = Process(target=_start_webserver) p.start() return p + @regular_app.post("/v1/algo/{username}/{algoname}") async def process_algo_req(request: Request, username, algoname, output: Optional[str] = None): metadata = {"request_id": "req-55c0480d-6af3-4a21-990a-5c51d29f5725", "duration": 0.000306774} @@ -60,6 +59,29 @@ async def process_hello_world(request: Request, username, algoname, githash): ### Algorithm Routes +@regular_app.get('/v1/algorithms/{username}/{algoname}') +async def process_get_algo(request: Request, username, algoname): + if algoname == "echo": + return {"id": "21df7a38-eab8-4ac8-954c-41a285535e69", "name": "echo", + "details": {"summary": "", "label": "echo", "tagline": ""}, + "settings": {"algorithm_callability": "public", "source_visibility": "closed", + "package_set": "python36", "license": "apl", "royalty_microcredits": 0, + "network_access": "full", "pipeline_enabled": True, "insights_enabled": False, + "algorithm_environment": "067110e7-8969-4441-b3d6-5333f18a3db3"}, + "version_info": {"semantic_version": "0.1.0", "git_hash": "0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", + "version_uuid": "e06d2808-bb5e-46ae-b7bc-f3d9968e3c6b"}, + "build": {"build_id": "a9ae2c93-6f4e-42c0-ac54-baa4a66e53d3", "status": "succeeded", + "commit_sha": "0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", + "started_at": "2022-05-08T22:43:09.050Z", "finished_at": "2022-05-08T22:43:28.646Z", + "version_info": {"semantic_version": "0.1.0"}, "resource_type": "algorithm_build"}, + "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, + "compilation": {"successful": True, "output": ""}, + "self_link": "https://api.algorithmia.com/v1/algorithms/quality/echo/versions/0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", + "resource_type": "algorithm"} + else: + return {"error": "No such algorithm"} + + @regular_app.get("/v1/algorithms/{username}/{algoname}/builds/{buildid}") async def get_build_id(username, algoname, buildid): return {"status": "succeeded", "build_id": buildid, "commit_sha": "bcdadj", @@ -71,6 +93,7 @@ async def get_build_id(username, algoname, buildid): async def get_build_log(username, algoname, buildid): return {"logs": "This is a log"} + @regular_app.get("/v1/algorithms/{username}/{algoname}/scm/status") async def get_scm_status(username, algoname): return {"scm_connection_status": "active"} @@ -152,50 +175,47 @@ async def publish_algorithm(request: Request, username, algoname): "resource_type": "algorithm"} +@regular_app.get("/v1/algorithms/{username}/{algoname}/versions") +async def versions_of_algorithm(request: Request, username, algoname): + return {"marker": None, "next_link": None, "results": [ + {"id": "21df7a38-eab8-4ac8-954c-41a285535e69", "name": algoname, + "details": {"summary": "", "label": algoname, "tagline": ""}, + "settings": {"algorithm_callability": "public", "source_visibility": "closed", "package_set": "python36", + "license": "apl", "royalty_microcredits": 0, "network_access": "full", "pipeline_enabled": True, + "insights_enabled": False, "algorithm_environment": "067110e7-8969-4441-b3d6-5333f18a3db3"}, + "version_info": {"semantic_version": "0.1.0", "git_hash": "0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", + "version_uuid": "e06d2808-bb5e-46ae-b7bc-f3d9968e3c6b"}, + "build": {"build_id": "a9ae2c93-6f4e-42c0-ac54-baa4a66e53d3", "status": "succeeded", + "commit_sha": "0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", "started_at": "2022-05-08T22:43:09.050Z", + "finished_at": "2022-05-08T22:43:28.646Z", "version_info": {"semantic_version": "0.1.0"}, + "resource_type": "algorithm_build"}, + "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, + "compilation": {"successful": True}, + "self_link": f"https://api.algorithmia.com/v1/algorithms/{username}/{algoname}/versions" + "/0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", + "resource_type": "algorithm"}]} + + @regular_app.get("/v1/algorithms/{username}/{algoname}/versions/{algohash}") async def get_algorithm_info(username, algoname, algohash): - return { - "id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", - "name": algoname, - "details": { - "summary": "Example Summary", - "label": "QA", - "tagline": "Example Tagline" - }, - "settings": { - "algorithm_callability": "private", - "source_visibility": "open", - "language": "python3", - "environment": "gpu", - "package_set": "tensorflow-gpu-2.3-python38", - "license": "apl", - "network_access": "isolated", - "pipeline_enabled": False, - "insights_enabled": False, - "algorithm_environment": "fd980f4f-1f1c-4b2f-a128-d60b40c6567a" - }, - "version_info": { - "semantic_version": "0.1.0", - "git_hash": algohash, - "release_notes": "created programmatically", - "sample_input": "\"payload\"", - "sample_output": "Exception encountered while running sample input", - "version_uuid": "1d9cb91d-11ca-49cb-a7f4-28f67f277654" - }, - "source": { - "scm": { - "id": "internal", - "provider": "internal", - "default": True, - "enabled": True - } - }, - "compilation": { - "successful": True, - "output": "" - }, - "resource_type": "algorithm" - } + if algohash == "e85db9bca2fad519f540b445f30d12523e4dec9c": + return {"id": "21df7a38-eab8-4ac8-954c-41a285535e69", "name": algoname, + "details": {"summary": "", "label": algoname, "tagline": ""}, + "settings": {"algorithm_callability": "public", "source_visibility": "closed", "language": "python3", + "environment": "cpu", "package_set": "python36", "license": "apl", + "royalty_microcredits": 0, "network_access": "full", "pipeline_enabled": True, + "insights_enabled": False, + "algorithm_environment": "067110e7-8969-4441-b3d6-5333f18a3db3"}, + "version_info": {"semantic_version": "0.1.0", "git_hash": "0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", + "version_uuid": "e06d2808-bb5e-46ae-b7bc-f3d9968e3c6b"}, + "build": {"build_id": "a9ae2c93-6f4e-42c0-ac54-baa4a66e53d3", "status": "succeeded", + "commit_sha": "0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", + "started_at": "2022-05-08T22:43:09.050Z", "finished_at": "2022-05-08T22:43:28.646Z", + "version_info": {"semantic_version": "0.1.0"}, "resource_type": "algorithm_build"}, + "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, + "compilation": {"successful": True, "output": ""}, "resource_type": "algorithm"} + else: + return {"error": {"message": "not found"}} ### Admin Routes @@ -287,6 +307,7 @@ async def get_org_by_name(org_name): "self_link": "http://localhost:8080/v1/organizations/a_myOrg1542" } + @regular_app.get("/v1/algorithms/{username}/{algoname}/builds/{buildid}/logs") async def get_build_log(username, algoname, buildid): return {"logs": "This is a log"} @@ -350,6 +371,3 @@ async def get_environments_by_lang(language): } ] } - - - \ No newline at end of file diff --git a/Test/api/self_signed_app.py b/Test/api/self_signed_app.py index 4d1423f..75ff3cc 100644 --- a/Test/api/self_signed_app.py +++ b/Test/api/self_signed_app.py @@ -18,6 +18,7 @@ def _start_webserver(): p.start() return p + @self_signed_app.post("/v1/algo/{username}/{algoname}") async def process_algo_req(request: Request, username, algoname, output: Optional[str] = None): metadata = {"request_id": "req-55c0480d-6af3-4a21-990a-5c51d29f5725", "duration": 0.000306774} @@ -59,6 +60,28 @@ async def process_hello_world(request: Request, username, algoname, githash): ### Algorithm Routes +@self_signed_app.get('/v1/algorithms/{username}/{algoname}') +async def process_get_algo(request: Request, username, algoname): + if algoname == "echo": + return {"id": "21df7a38-eab8-4ac8-954c-41a285535e69", "name": "echo", + "details": {"summary": "", "label": "echo", "tagline": ""}, + "settings": {"algorithm_callability": "public", "source_visibility": "closed", + "package_set": "python36", "license": "apl", "royalty_microcredits": 0, + "network_access": "full", "pipeline_enabled": True, "insights_enabled": False, + "algorithm_environment": "067110e7-8969-4441-b3d6-5333f18a3db3"}, + "version_info": {"semantic_version": "0.1.0", "git_hash": "0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", + "version_uuid": "e06d2808-bb5e-46ae-b7bc-f3d9968e3c6b"}, + "build": {"build_id": "a9ae2c93-6f4e-42c0-ac54-baa4a66e53d3", "status": "succeeded", + "commit_sha": "0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", + "started_at": "2022-05-08T22:43:09.050Z", "finished_at": "2022-05-08T22:43:28.646Z", + "version_info": {"semantic_version": "0.1.0"}, "resource_type": "algorithm_build"}, + "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, + "compilation": {"successful": True, "output": ""}, + "self_link": "https://api.algorithmia.com/v1/algorithms/quality/echo/versions/0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", + "resource_type": "algorithm"} + else: + return {"error":"No such algorithm"} + @self_signed_app.get("/v1/algorithms/{username}/{algoname}/builds/{buildid}") async def get_build_id(username, algoname, buildid): return {"status": "succeeded", "build_id": buildid, "commit_sha": "bcdadj", diff --git a/Test/regular/algo_test.py b/Test/regular/algo_test.py index 0e3afdd..e54dfd4 100644 --- a/Test/regular/algo_test.py +++ b/Test/regular/algo_test.py @@ -3,6 +3,7 @@ from Algorithmia.errors import AlgorithmException from Algorithmia.algorithm import OutputType import Algorithmia + # look in ../ BEFORE trying to import Algorithmia. If you append to the # you will load the version installed on the computer. sys.path = ['../'] + sys.path @@ -11,59 +12,72 @@ if sys.version_info.major >= 3: - class AlgoDummyTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.client = Algorithmia.client(api_address="http://localhost:8080", api_key="simabcd123") def test_call_customCert(self): - result = self.client.algo('util/echo').pipe(bytearray('foo', 'utf-8')) + result = self.client.algo('quality/echo').pipe(bytearray('foo', 'utf-8')) self.assertEquals('binary', result.metadata.content_type) self.assertEquals(bytearray('foo', 'utf-8'), result.result) def test_normal_call(self): - result = self.client.algo('util/echo').pipe("foo") + result = self.client.algo('quality/echo').pipe("foo") self.assertEquals("text", result.metadata.content_type) self.assertEquals("foo", result.result) def test_async_call(self): - result = self.client.algo('util/echo').set_options(output=OutputType.void).pipe("foo") + result = self.client.algo('quality/echo').set_options(output=OutputType.void).pipe("foo") self.assertTrue(hasattr(result, "async_protocol")) self.assertTrue(hasattr(result, "request_id")) def test_raw_call(self): - result = self.client.algo('util/echo').set_options(output=OutputType.raw).pipe("foo") + result = self.client.algo('quality/echo').set_options(output=OutputType.raw).pipe("foo") self.assertEquals("foo", result) def test_dict_call(self): - result = self.client.algo('util/echo').pipe({"foo": "bar"}) + result = self.client.algo('quality/echo').pipe({"foo": "bar"}) self.assertEquals("json", result.metadata.content_type) self.assertEquals({"foo": "bar"}, result.result) + def test_algo_exists(self): + result = self.client.algo('quality/echo').exists() + self.assertEquals(True, result) + + def test_algo_no_exists(self): + result = self.client.algo('quality/not_echo').exists() + self.assertEquals(False, result) + def test_text_unicode(self): telephone = u"\u260E" # Unicode input to pipe() - result1 = self.client.algo('util/Echo').pipe(telephone) + result1 = self.client.algo('quality/echo').pipe(telephone) self.assertEquals('text', result1.metadata.content_type) self.assertEquals(telephone, result1.result) # Unicode return in .result - result2 = self.client.algo('util/Echo').pipe(result1.result) + result2 = self.client.algo('quality/echo').pipe(result1.result) self.assertEquals('text', result2.metadata.content_type) self.assertEquals(telephone, result2.result) + + def test_algo_info(self): + result = self.client.algo('quality/echo').info() + self.assertTrue('results' in result) + self.assertTrue('resource_type' in result['results'][0]) + self.assertTrue(result['results'][0]['resource_type'] == "algorithm") def test_get_build_by_id(self): - result = self.client.algo("J_bragg/Echo").get_build("1a392e2c-b09f-4bae-a616-56c0830ac8e5") - self.assertTrue(result.build_id is not None) + result = self.client.algo("quality/echo").get_build("1a392e2c-b09f-4bae-a616-56c0830ac8e5") + self.assertTrue('commit_sha' in result) def test_get_build_logs(self): - result = self.client.algo("J_bragg/Echo").get_build_logs("1a392e2c-b09f-4bae-a616-56c0830ac8e5") - self.assertTrue(result.logs is not None) + result = self.client.algo("quality/echo").get_build_logs("1a392e2c-b09f-4bae-a616-56c0830ac8e5") + self.assertTrue('logs' in result) def test_get_scm_status(self): - result = self.client.algo("J_bragg/Echo").get_scm_status() - self.assertTrue(result.scm_connection_status is not None) + result = self.client.algo("quality/echo").get_scm_status() + self.assertTrue('scm_connection_status' in result) def test_exception_ipa_algo(self): try: @@ -79,7 +93,7 @@ def setUp(self): def test_call_customCert(self): open("./test.pem", 'w') c = Algorithmia.client(ca_cert="./test.pem") - result = c.algo('util/Echo').pipe(bytearray('foo', 'utf-8')) + result = c.algo('quality/echo').pipe(bytearray('foo', 'utf-8')) self.assertEquals('binary', result.metadata.content_type) self.assertEquals(bytearray('foo', 'utf-8'), result.result) try: @@ -88,43 +102,36 @@ def test_call_customCert(self): print(e) def test_call_binary(self): - result = self.client.algo('util/Echo').pipe(bytearray('foo', 'utf-8')) + result = self.client.algo('quality/echo').pipe(bytearray('foo', 'utf-8')) self.assertEquals('binary', result.metadata.content_type) self.assertEquals(bytearray('foo', 'utf-8'), result.result) def test_async_call(self): - result = self.client.algo('util/echo').set_options(output=OutputType.void).pipe("foo") + result = self.client.algo('quality/echo').set_options(output=OutputType.void).pipe("foo") self.assertTrue(hasattr(result, "async_protocol")) self.assertTrue(hasattr(result, "request_id")) def test_raw_call(self): - result = self.client.algo('util/echo').set_options(output=OutputType.raw).pipe("foo") + result = self.client.algo('quality/echo').set_options(output=OutputType.raw).pipe("foo") self.assertEquals("foo", result) def test_text_unicode(self): telephone = u"\u260E" # Unicode input to pipe() - result1 = self.client.algo('util/Echo').pipe(telephone) + result1 = self.client.algo('quality/echo').pipe(telephone) self.assertEquals('text', result1.metadata.content_type) self.assertEquals(telephone, result1.result) # Unicode return in .result - result2 = self.client.algo('util/Echo').pipe(result1.result) + result2 = self.client.algo('quality/echo').pipe(result1.result) self.assertEquals('text', result2.metadata.content_type) self.assertEquals(telephone, result2.result) - def test_get_build_by_id(self): - result = self.client.algo("J_bragg/Echo").get_build("1a392e2c-b09f-4bae-a616-56c0830ac8e5") - self.assertTrue(result.build_id is not None) - - def test_get_build_logs(self): - result = self.client.algo("J_bragg/Echo").get_build_logs("1a392e2c-b09f-4bae-a616-56c0830ac8e5") - self.assertTrue(result.logs is not None) def test_get_scm_status(self): - result = self.client.algo("J_bragg/Echo").get_scm_status() - self.assertTrue(result.scm_connection_status is not None) + result = self.client.algo("quality/echo").get_scm_status() + self.assertTrue('scm_connection_status' in result) def test_exception_ipa_algo(self): try: diff --git a/Test/regular/client_test.py b/Test/regular/client_test.py index 8055e51..ea3c566 100644 --- a/Test/regular/client_test.py +++ b/Test/regular/client_test.py @@ -61,17 +61,6 @@ def test_get_environment(self): if u'error' not in response: self.assertTrue(response is not None and u'environments' in response) - def test_get_build_logs(self): - user = unicode(os.environ.get('ALGO_USER_NAME')) - algo = unicode('echo') - algo_path = u'%s/%s' % (user, algo) - result = self.client.algo(algo_path).build_logs() - - if u'error' in result: - print(result) - - self.assertTrue(u'error' not in result) - def test_edit_org(self): org_name = "a_myOrg84" @@ -151,16 +140,21 @@ def test_algorithm_programmatic_create_process(self): "network_access": "isolated", "pipeline_enabled": False } + version_info = { + "sample_input": "hello" + } created_algo = self.client.algo(full_path) - response = created_algo.create(details=details, settings=settings) + print("about to create algo") + response = created_algo.create(details=details, settings=settings, version_info=version_info) + print("created algo") self.assertEqual(response.name, algorithm_name, "algorithm creation failed") # --- Creation complete, compiling response = created_algo.compile() - git_hash = response.version_info.git_hash + git_hash = response['version_info']['git_hash'] algo_with_build = self.client.algo(full_path + "/" + git_hash) - self.assertEqual(response.name, created_algo.algoname) + self.assertEqual(response['name'], created_algo.algoname) # --- compiling complete, now testing algorithm request response = algo_with_build.pipe(payload).result @@ -188,7 +182,7 @@ def test_algorithm_programmatic_create_process(self): response = created_algo.info(git_hash) - self.assertEqual(response.version_info.semantic_version, "0.1.0", "information is incorrect") + self.assertEqual(response.version_info['semantic_version'], "0.1.0", "information is incorrect") def test_no_auth_client(self): @@ -262,17 +256,6 @@ def test_get_environment(self): if u'error' not in response: self.assertTrue(response is not None and u'environments' in response) - def test_get_build_logs(self): - user = unicode(os.environ.get('ALGO_USER_NAME')) - algo = unicode('echo') - algo_path = u'%s/%s' % (user, algo) - result = self.regular_client.algo(algo_path).build_logs() - - if u'error' in result: - print(result) - - self.assertTrue(u'error' not in result) - def test_edit_org(self): org_name = "a_myOrg84" @@ -348,62 +331,6 @@ def test_get_algorithm_errors(self): else: self.assertEqual(404, response.status_code) - def test_algorithm_programmatic_create_process(self): - algorithm_name = "algo_" + str(uuid4()).split("-")[-1] - payload = "John" - expected_response = "hello John" - full_path = self.regular_client.username() + "/" + algorithm_name - details = { - "summary": "Example Summary", - "label": "QA", - "tagline": "Example Tagline" - } - settings = { - "source_visibility": "open", - "algorithm_environment": self.environment_id, - "license": "apl", - "network_access": "isolated", - "pipeline_enabled": False - } - created_algo = self.regular_client.algo(full_path) - response = created_algo.create(details=details, settings=settings) - self.assertEqual(response.name, algorithm_name, "algorithm creation failed") - - # --- Creation complete, compiling - - response = created_algo.compile() - git_hash = response.version_info.git_hash - algo_with_build = self.regular_client.algo(full_path + "/" + git_hash) - self.assertEqual(response.name, created_algo.algoname) - - # --- compiling complete, now testing algorithm request - response = algo_with_build.pipe(payload).result - self.assertEqual(response, expected_response, "compiling failed") - - # --- testing complete, now publishing new release. - - pub_settings = {"algorithm_callability": "private"} - pub_version_info = { - "release_notes": "created programmatically", - "sample_input": payload, - "version_type": "minor" - } - pub_details = {"label": "testing123"} - - response = algo_with_build.publish( - details=pub_details, - settings=pub_settings, - version_info=pub_version_info - ) - self.assertEqual(response["version_info"]["semantic_version"], "0.1.0", - "Publishing failed, semantic version is not correct.") - - # --- publishing complete, getting additional information - - response = created_algo.info(git_hash) - - self.assertEqual(response.version_info.semantic_version, "0.1.0", "information is incorrect") - def test_algo_freeze(self): self.regular_client.freeze("Test/resources/manifests/example_manifest.json", "Test/resources/manifests") diff --git a/Test/self_signed/acl_test.py b/Test/self_signed/acl_test.py deleted file mode 100644 index 2022c65..0000000 --- a/Test/self_signed/acl_test.py +++ /dev/null @@ -1,38 +0,0 @@ -import sys -# look in ../ BEFORE trying to import Algorithmia. If you append to the -# you will load the version installed on the computer. -sys.path = ['../'] + sys.path - -import unittest -import Algorithmia -from Algorithmia.acl import AclType, Acl, ReadAcl -from Algorithmia.datadirectory import DataDirectory - -class AclTypeTest(unittest.TestCase): - def test_types(self): - self.assertTrue(AclType.private.acl_string is None) - self.assertEquals(AclType.my_algos.acl_string, 'algo://.my/*') - self.assertEquals(AclType.public.acl_string, 'user://*') - self.assertEquals(AclType.default, AclType.my_algos) - - def test_from_acl_response(self): - self.assertEquals(AclType.from_acl_response([]), AclType.private) - self.assertEquals(AclType.from_acl_response(['algo://.my/*']), AclType.my_algos) - self.assertEquals(AclType.from_acl_response(['user://*']), AclType.public) - - def test_create_acl(self): - c = Algorithmia.client() - dd = DataDirectory(c, 'data://.my/privatePermissions') - if dd.exists(): - dd.delete(True) - dd.create(ReadAcl.private) - - dd_perms = DataDirectory(c, 'data://.my/privatePermissions').get_permissions() - self.assertEquals(dd_perms.read_acl, AclType.private) - - dd.update_permissions(ReadAcl.public) - dd_perms = DataDirectory(c, 'data://.my/privatePermissions').get_permissions() - self.assertEquals(dd_perms.read_acl, AclType.public) - -if __name__ == '__main__': - unittest.main() diff --git a/Test/self_signed/algo_test.py b/Test/self_signed/algo_test.py index a5e0964..3c6f54c 100644 --- a/Test/self_signed/algo_test.py +++ b/Test/self_signed/algo_test.py @@ -3,6 +3,7 @@ from Algorithmia.errors import AlgorithmException from Algorithmia.algorithm import OutputType import Algorithmia + # look in ../ BEFORE trying to import Algorithmia. If you append to the # you will load the version installed on the computer. sys.path = ['../'] + sys.path @@ -11,7 +12,6 @@ if sys.version_info.major >= 3: - class AlgoDummyTest(unittest.TestCase): @classmethod def setUpClass(cls): @@ -55,75 +55,17 @@ def test_text_unicode(self): def test_get_build_by_id(self): result = self.client.algo("J_bragg/Echo").get_build("1a392e2c-b09f-4bae-a616-56c0830ac8e5") - self.assertTrue(result.build_id is not None) - - def test_get_build_logs(self): - result = self.client.algo("J_bragg/Echo").get_build_logs("1a392e2c-b09f-4bae-a616-56c0830ac8e5") - self.assertTrue(result.logs is not None) - - def test_get_scm_status(self): - result = self.client.algo("J_bragg/Echo").get_scm_status() - self.assertTrue(result.scm_connection_status is not None) - - def test_exception_ipa_algo(self): - try: - result = self.client.algo('zeryx/raise_exception').pipe("") - except AlgorithmException as e: - self.assertEqual(e.message, "This is an exception") - -else: - class AlgoTest(unittest.TestCase): - def setUp(self): - self.client = Algorithmia.client() - - def test_call_customCert(self): - open("./test.pem", 'w') - c = Algorithmia.client(ca_cert="./test.pem") - result = c.algo('util/Echo').pipe(bytearray('foo', 'utf-8')) - self.assertEquals('binary', result.metadata.content_type) - self.assertEquals(bytearray('foo', 'utf-8'), result.result) - try: - os.remove("./test.pem") - except OSError as e: - print(e) - - def test_call_binary(self): - result = self.client.algo('util/Echo').pipe(bytearray('foo', 'utf-8')) - self.assertEquals('binary', result.metadata.content_type) - self.assertEquals(bytearray('foo', 'utf-8'), result.result) - - def test_async_call(self): - result = self.client.algo('util/echo').set_options(output=OutputType.void).pipe("foo") - self.assertTrue(hasattr(result, "async_protocol")) - self.assertTrue(hasattr(result, "request_id")) - - def test_raw_call(self): - result = self.client.algo('util/echo').set_options(output=OutputType.raw).pipe("foo") - self.assertEquals("foo", result) - - def test_text_unicode(self): - telephone = u"\u260E" - - # Unicode input to pipe() - result1 = self.client.algo('util/Echo').pipe(telephone) - self.assertEquals('text', result1.metadata.content_type) - self.assertEquals(telephone, result1.result) - - # Unicode return in .result - result2 = self.client.algo('util/Echo').pipe(result1.result) - self.assertEquals('text', result2.metadata.content_type) - self.assertEquals(telephone, result2.result) - - def test_get_build_by_id(self): - result = self.client.algo("J_bragg/Echo").get_build("1a392e2c-b09f-4bae-a616-56c0830ac8e5") - self.assertTrue(result.build_id is not None) + print(result) + self.assertTrue(result.commit_sha is not None) def test_get_build_logs(self): result = self.client.algo("J_bragg/Echo").get_build_logs("1a392e2c-b09f-4bae-a616-56c0830ac8e5") + print(result) self.assertTrue(result.logs is not None) def test_get_scm_status(self): result = self.client.algo("J_bragg/Echo").get_scm_status() + print(result) self.assertTrue(result.scm_connection_status is not None) def test_exception_ipa_algo(self): diff --git a/Test/self_signed/client_test.py b/Test/self_signed/client_test.py deleted file mode 100644 index fc863ee..0000000 --- a/Test/self_signed/client_test.py +++ /dev/null @@ -1,413 +0,0 @@ -import os -import shutil -import sys -from datetime import datetime -from random import random -from random import seed - -sys.path = ['../'] + sys.path - -import unittest -import Algorithmia -from Algorithmia.errors import AlgorithmException -from uuid import uuid4 - -if sys.version_info.major >= 3: - unicode = str - - - class ClientDummyTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.client = Algorithmia.client(api_address="https://localhost:8090", api_key="simabcd123", ca_cert=False) - - admin_username = "a_Mrtest" - admin_org_name = "a_myOrg" - environment_name = "Python 3.9" - - def setUp(self): - self.admin_username = self.admin_username + str(int(random() * 10000)) - self.admin_org_name = self.admin_org_name + str(int(random() * 10000)) - - self.environment_id = "abcd-123" - - def test_create_user(self): - response = self.client.create_user( - {"username": self.admin_username, "email": self.admin_username + "@algo.com", "passwordHash": "", - "shouldCreateHello": False}) - - if type(response) is dict: - self.assertEqual(self.admin_username, response['username']) - else: - self.assertIsNotNone(response) - - def test_get_org_types(self): - response = self.client.get_org_types() - self.assertTrue(len(response) > 0) - - def test_create_org(self): - response = self.client.create_org( - {"org_name": self.admin_org_name, "org_label": "some label", "org_contact_name": "Some owner", - "org_email": self.admin_org_name + "@algo.com", "type_id": "basic"}) - - self.assertEqual(self.admin_org_name, response[u'org_name']) - - def test_get_org(self): - response = self.client.get_org("a_myOrg84") - self.assertEqual("a_myOrg84", response['org_name']) - - def test_get_environment(self): - response = self.client.get_environment("python2") - - if u'error' not in response: - self.assertTrue(response is not None and u'environments' in response) - - def test_get_build_logs(self): - user = unicode(os.environ.get('ALGO_USER_NAME')) - algo = unicode('echo') - algo_path = u'%s/%s' % (user, algo) - result = self.client.algo(algo_path).build_logs() - - if u'error' in result: - print(result) - - self.assertTrue(u'error' not in result) - - def test_edit_org(self): - org_name = "a_myOrg84" - - obj = { - "id": "b85d8c4e-7f3c-40b9-9659-6adc2cb0e16f", - "org_name": "a_myOrg84", - "org_label": "some label", - "org_contact_name": "Some owner", - "org_email": "a_myOrg84@algo.com", - "org_created_at": "2020-11-30T23:51:40", - "org_url": "https://algorithmia.com", - "type_id": "basic", - "resource_type": "organization" - } - - response = self.client.edit_org(org_name, obj) - if type(response) is dict: - print(response) - else: - self.assertEqual(204, response.status_code) - - def test_get_supported_languages(self): - response = self.client.get_supported_languages() - self.assertTrue(response is not None) - - if type(response) is not list: - self.assertTrue(u'error' in response) - else: - language_found = any('anaconda3' in languages['name'] for languages in response) - self.assertTrue(response is not None and language_found) - - def test_invite_to_org(self): - response = self.client.invite_to_org("a_myOrg38", "a_Mrtest4") - if type(response) is dict: - self.assertTrue(u'error' in response) - else: - self.assertEqual(200, response.status_code) - - # This test will require updating after the /v1/organizations/{org_name}/errors endpoint has been - # deployed to the remote environment. - def test_get_organization_errors(self): - response = self.client.get_organization_errors(self.admin_org_name) - self.assertTrue(response is not None) - - if type(response) is list: - self.assertEqual(0, len(response), 'Received unexpected result, should have been 0.') - - def test_get_user_errors(self): - response = self.client.get_user_errors(self.admin_username) - - self.assertTrue(response is not None) - self.assertEqual(0, len(response)) - - - def test_get_algorithm_errors(self): - response = self.client.get_algorithm_errors('hello') - self.assertTrue(response is not None) - - if type(response) is dict: - self.assertTrue(u'error' in response) - else: - self.assertEqual(404, response.status_code) - - def test_algorithm_programmatic_create_process(self): - algorithm_name = "algo_e2d_test" - payload = "John" - expected_response = "hello John" - full_path = "a_Mrtest/" + algorithm_name - details = { - "summary": "Example Summary", - "label": "QA", - "tagline": "Example Tagline" - } - settings = { - "source_visibility": "open", - "algorithm_environment": self.environment_id, - "license": "apl", - "network_access": "isolated", - "pipeline_enabled": False - } - created_algo = self.client.algo(full_path) - response = created_algo.create(details=details, settings=settings) - self.assertEqual(response.name, algorithm_name, "algorithm creation failed") - - # --- Creation complete, compiling - - response = created_algo.compile() - git_hash = response.version_info.git_hash - algo_with_build = self.client.algo(full_path + "/" + git_hash) - self.assertEqual(response.name, created_algo.algoname) - - # --- compiling complete, now testing algorithm request - response = algo_with_build.pipe(payload).result - self.assertEqual(response, expected_response, "compiling failed") - - # --- testing complete, now publishing new release. - - pub_settings = {"algorithm_callability": "private"} - pub_version_info = { - "release_notes": "created programmatically", - "sample_input": payload, - "version_type": "minor" - } - pub_details = {"label": "testing123"} - - response = algo_with_build.publish( - details=pub_details, - settings=pub_settings, - version_info=pub_version_info - ) - self.assertEqual(response["version_info"]["semantic_version"], "0.1.0", - "Publishing failed, semantic version is not correct.") - - # --- publishing complete, getting additional information - - response = created_algo.info(git_hash) - - self.assertEqual(response.version_info.semantic_version, "0.1.0", "information is incorrect") - - def test_no_auth_client(self): - - key = os.environ.get('ALGORITHMIA_API_KEY', "") - if key != "": - del os.environ['ALGORITHMIA_API_KEY'] - - client = Algorithmia.client(api_address="http://localhost:8080") - error = None - try: - client.algo("demo/hello").pipe("world") - except Exception as e: - error = e - finally: - os.environ['ALGORITHMIA_API_KEY'] = key - self.assertEqual(str(error), str(AlgorithmException(message="authorization required", stack_trace=None, error_type=None))) - -else: - class ClientTest(unittest.TestCase): - seed(datetime.now().microsecond) - # due to legacy reasons, regular client tests are tested against api.algorithmia.com, whereas admin api tests - # are run against test.algorithmia.com. - admin_username = "quality" - admin_org_name = "a_myOrg" - environment_name = "Python 3.9" - - def setUp(self): - self.admin_api_key = unicode(os.environ.get('ALGORITHMIA_A_KEY')) - self.regular_api_key = unicode(os.environ.get('ALGORITHMIA_API_KEY')) - - self.admin_username = self.admin_username + str(int(random() * 10000)) - self.admin_org_name = self.admin_org_name + str(int(random() * 10000)) - self.admin_client = Algorithmia.client(api_address="https://api.algorithmia.com", - api_key=self.admin_api_key) - self.regular_client = Algorithmia.client(api_address='https://api.algorithmia.com', - api_key=self.regular_api_key) - - environments = self.regular_client.get_environment("python3") - for environment in environments['environments']: - if environment['display_name'] == self.environment_name: - self.environment_id = environment['id'] - - def test_create_user(self): - response = self.admin_client.create_user( - {"username": self.admin_username, "email": self.admin_username + "@algo.com", "passwordHash": "", - "shouldCreateHello": False}) - - if type(response) is dict: - self.assertEqual(self.admin_username, response['username']) - else: - self.assertIsNotNone(response) - - def test_get_org_types(self): - response = self.admin_client.get_org_types() - self.assertTrue(len(response) > 0) - - def test_create_org(self): - response = self.admin_client.create_org( - {"org_name": self.admin_org_name, "org_label": "some label", "org_contact_name": "Some owner", - "org_email": self.admin_org_name + "@algo.com", "type_id": "basic"}) - - self.assertEqual(self.admin_org_name, response[u'org_name']) - - def test_get_org(self): - response = self.admin_client.get_org("a_myOrg84") - self.assertEqual("a_myOrg84", response['org_name']) - - def test_get_environment(self): - response = self.admin_client.get_environment("python2") - - if u'error' not in response: - self.assertTrue(response is not None and u'environments' in response) - - def test_get_build_logs(self): - user = unicode(os.environ.get('ALGO_USER_NAME')) - algo = unicode('echo') - algo_path = u'%s/%s' % (user, algo) - result = self.regular_client.algo(algo_path).build_logs() - - if u'error' in result: - print(result) - - self.assertTrue(u'error' not in result) - - def test_edit_org(self): - org_name = "a_myOrg84" - - obj = { - "id": "b85d8c4e-7f3c-40b9-9659-6adc2cb0e16f", - "org_name": "a_myOrg84", - "org_label": "some label", - "org_contact_name": "Some owner", - "org_email": "a_myOrg84@algo.com", - "org_created_at": "2020-11-30T23:51:40", - "org_url": "https://algorithmia.com", - "type_id": "basic", - "resource_type": "organization" - } - - response = self.admin_client.edit_org(org_name, obj) - if type(response) is dict: - print(response) - else: - self.assertEqual(204, response.status_code) - - def test_get_template(self): - filename = "./temptest" - response = self.admin_client.get_template("36fd467e-fbfe-4ea6-aa66-df3f403b7132", filename) - - if type(response) is dict: - self.assertTrue(u'error' in response or u'message' in response) - else: - self.assertTrue(response.ok) - try: - shutil.rmtree(filename) - except OSError as e: - print(e) - - def test_get_supported_languages(self): - response = self.admin_client.get_supported_languages() - self.assertTrue(response is not None) - - if type(response) is not list: - self.assertTrue(u'error' in response) - else: - language_found = any('anaconda3' in languages['name'] for languages in response) - self.assertTrue(response is not None and language_found) - - def test_invite_to_org(self): - response = self.admin_client.invite_to_org("a_myOrg38", "a_Mrtest4") - if type(response) is dict: - self.assertTrue(u'error' in response) - else: - self.assertEqual(200, response.status_code) - - # This test will require updating after the /v1/organizations/{org_name}/errors endpoint has been - # deployed to the remote environment. - def test_get_organization_errors(self): - response = self.admin_client.get_organization_errors(self.admin_org_name) - self.assertTrue(response is not None) - - if type(response) is list: - self.assertEqual(0, len(response), 'Received unexpected result, should have been 0.') - - def test_get_user_errors(self): - response = self.admin_client.get_user_errors(self.admin_username) - - self.assertTrue(response is not None) - self.assertEqual(0, len(response)) - - def test_get_algorithm_errors(self): - response = self.admin_client.get_algorithm_errors('hello') - self.assertTrue(response is not None) - - if type(response) is dict: - self.assertTrue(u'error' in response) - else: - self.assertEqual(404, response.status_code) - - def test_algorithm_programmatic_create_process(self): - algorithm_name = "algo_" + str(uuid4()).split("-")[-1] - payload = "John" - expected_response = "hello John" - full_path = self.regular_client.username() + "/" + algorithm_name - details = { - "summary": "Example Summary", - "label": "QA", - "tagline": "Example Tagline" - } - settings = { - "source_visibility": "open", - "algorithm_environment": self.environment_id, - "license": "apl", - "network_access": "isolated", - "pipeline_enabled": False - } - created_algo = self.regular_client.algo(full_path) - response = created_algo.create(details=details, settings=settings) - self.assertEqual(response.name, algorithm_name, "algorithm creation failed") - - # --- Creation complete, compiling - - response = created_algo.compile() - git_hash = response.version_info.git_hash - algo_with_build = self.regular_client.algo(full_path + "/" + git_hash) - self.assertEqual(response.name, created_algo.algoname) - - # --- compiling complete, now testing algorithm request - response = algo_with_build.pipe(payload).result - self.assertEqual(response, expected_response, "compiling failed") - - # --- testing complete, now publishing new release. - - pub_settings = {"algorithm_callability": "private"} - pub_version_info = { - "release_notes": "created programmatically", - "sample_input": payload, - "version_type": "minor" - } - pub_details = {"label": "testing123"} - - response = algo_with_build.publish( - details=pub_details, - settings=pub_settings, - version_info=pub_version_info - ) - self.assertEqual(response["version_info"]["semantic_version"], "0.1.0", - "Publishing failed, semantic version is not correct.") - - # --- publishing complete, getting additional information - - response = created_algo.info(git_hash) - - self.assertEqual(response.version_info.semantic_version, "0.1.0", "information is incorrect") - - def test_algo_freeze(self): - self.regular_client.freeze("Test/resources/manifests/example_manifest.json", "Test/resources/manifests") - -if __name__ == '__main__': - unittest.main() From 62fa78cb9170c0fd469abca11a0a98aa4aeeae78 Mon Sep 17 00:00:00 2001 From: zeryx <1892175+zeryx@users.noreply.github.com> Date: Wed, 11 May 2022 15:52:36 -0300 Subject: [PATCH 02/10] added more test coverage for versions and info functions --- Test/regular/algo_test.py | 16 ++++++++++ Test/regular/client_test.py | 2 +- Test/self_signed/algo_test.py | 55 +++++++++++++++++++++++------------ 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/Test/regular/algo_test.py b/Test/regular/algo_test.py index e54dfd4..068f40f 100644 --- a/Test/regular/algo_test.py +++ b/Test/regular/algo_test.py @@ -49,6 +49,14 @@ def test_algo_no_exists(self): result = self.client.algo('quality/not_echo').exists() self.assertEquals(False, result) + #TODO: add more coverage examples to check kwargs + def test_get_versions(self): + result = self.client.algo('quality/echo').versions() + self.assertTrue('results' in result) + self.assertTrue('version_info' in result['results'][0]) + self.assertTrue('semantic_version' in result['results'][0]['version_info']) + self.assertEquals('0.1.0', result['results'][0]['version_info']['semantic_version']) + def test_text_unicode(self): telephone = u"\u260E" # Unicode input to pipe() @@ -115,6 +123,14 @@ def test_raw_call(self): result = self.client.algo('quality/echo').set_options(output=OutputType.raw).pipe("foo") self.assertEquals("foo", result) + #TODO: add more coverage examples to check kwargs + def test_get_versions(self): + result = self.client.algo('quality/echo').versions() + self.assertTrue('results' in result) + self.assertTrue('version_info' in result['results'][0]) + self.assertTrue('semantic_version' in result['results'][0]['version_info']) + self.assertEquals('0.1.0', result['results'][0]['version_info']['semantic_version']) + def test_text_unicode(self): telephone = u"\u260E" diff --git a/Test/regular/client_test.py b/Test/regular/client_test.py index ea3c566..a4e8730 100644 --- a/Test/regular/client_test.py +++ b/Test/regular/client_test.py @@ -182,7 +182,7 @@ def test_algorithm_programmatic_create_process(self): response = created_algo.info(git_hash) - self.assertEqual(response.version_info['semantic_version'], "0.1.0", "information is incorrect") + self.assertEqual(response['version_info']['semantic_version'], "0.1.0", "information is incorrect") def test_no_auth_client(self): diff --git a/Test/self_signed/algo_test.py b/Test/self_signed/algo_test.py index 3c6f54c..b3b377f 100644 --- a/Test/self_signed/algo_test.py +++ b/Test/self_signed/algo_test.py @@ -4,12 +4,12 @@ from Algorithmia.algorithm import OutputType import Algorithmia +import unittest + # look in ../ BEFORE trying to import Algorithmia. If you append to the # you will load the version installed on the computer. sys.path = ['../'] + sys.path -import unittest - if sys.version_info.major >= 3: class AlgoDummyTest(unittest.TestCase): @@ -18,55 +18,74 @@ def setUpClass(cls): cls.client = Algorithmia.client(api_address="https://localhost:8090", api_key="simabcd123", ca_cert=False) def test_call_customCert(self): - result = self.client.algo('util/echo').pipe(bytearray('foo', 'utf-8')) + result = self.client.algo('quality/echo').pipe(bytearray('foo', 'utf-8')) self.assertEquals('binary', result.metadata.content_type) self.assertEquals(bytearray('foo', 'utf-8'), result.result) def test_normal_call(self): - result = self.client.algo('util/echo').pipe("foo") + result = self.client.algo('quality/echo').pipe("foo") self.assertEquals("text", result.metadata.content_type) self.assertEquals("foo", result.result) def test_async_call(self): - result = self.client.algo('util/echo').set_options(output=OutputType.void).pipe("foo") + result = self.client.algo('quality/echo').set_options(output=OutputType.void).pipe("foo") self.assertTrue(hasattr(result, "async_protocol")) self.assertTrue(hasattr(result, "request_id")) def test_raw_call(self): - result = self.client.algo('util/echo').set_options(output=OutputType.raw).pipe("foo") + result = self.client.algo('quality/echo').set_options(output=OutputType.raw).pipe("foo") self.assertEquals("foo", result) def test_dict_call(self): - result = self.client.algo('util/echo').pipe({"foo": "bar"}) + result = self.client.algo('quality/echo').pipe({"foo": "bar"}) self.assertEquals("json", result.metadata.content_type) self.assertEquals({"foo": "bar"}, result.result) + def test_algo_exists(self): + result = self.client.algo('quality/echo').exists() + self.assertEquals(True, result) + + def test_algo_no_exists(self): + result = self.client.algo('quality/not_echo').exists() + self.assertEquals(False, result) + + # TODO: add more coverage examples to check kwargs + def test_get_versions(self): + result = self.client.algo('quality/echo').versions() + self.assertTrue('results' in result) + self.assertTrue('version_info' in result['results'][0]) + self.assertTrue('semantic_version' in result['results'][0]['version_info']) + self.assertEquals('0.1.0', result['results'][0]['version_info']['semantic_version']) + def test_text_unicode(self): telephone = u"\u260E" # Unicode input to pipe() - result1 = self.client.algo('util/Echo').pipe(telephone) + result1 = self.client.algo('quality/echo').pipe(telephone) self.assertEquals('text', result1.metadata.content_type) self.assertEquals(telephone, result1.result) # Unicode return in .result - result2 = self.client.algo('util/Echo').pipe(result1.result) + result2 = self.client.algo('quality/echo').pipe(result1.result) self.assertEquals('text', result2.metadata.content_type) self.assertEquals(telephone, result2.result) + def test_algo_info(self): + result = self.client.algo('quality/echo').info() + self.assertTrue('results' in result) + self.assertTrue('resource_type' in result['results'][0]) + self.assertTrue(result['results'][0]['resource_type'] == "algorithm") + def test_get_build_by_id(self): - result = self.client.algo("J_bragg/Echo").get_build("1a392e2c-b09f-4bae-a616-56c0830ac8e5") - print(result) - self.assertTrue(result.commit_sha is not None) + result = self.client.algo("quality/echo").get_build("1a392e2c-b09f-4bae-a616-56c0830ac8e5") + self.assertTrue('commit_sha' in result) def test_get_build_logs(self): - result = self.client.algo("J_bragg/Echo").get_build_logs("1a392e2c-b09f-4bae-a616-56c0830ac8e5") - print(result) - self.assertTrue(result.logs is not None) + result = self.client.algo("quality/echo").get_build_logs("1a392e2c-b09f-4bae-a616-56c0830ac8e5") + self.assertTrue('logs' in result) def test_get_scm_status(self): - result = self.client.algo("J_bragg/Echo").get_scm_status() - print(result) - self.assertTrue(result.scm_connection_status is not None) + result = self.client.algo("quality/echo").get_scm_status() + self.assertTrue('scm_connection_status' in result) def test_exception_ipa_algo(self): try: From 12a3aaf264dfce0d42167ad27b8b3b293c02c9fd Mon Sep 17 00:00:00 2001 From: zeryx <1892175+zeryx@users.noreply.github.com> Date: Wed, 11 May 2022 16:00:07 -0300 Subject: [PATCH 03/10] removed unnecessary class conversion function --- Algorithmia/util.py | 5 ----- Test/regular/client_test.py | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Algorithmia/util.py b/Algorithmia/util.py index 27cf236..92aa3b3 100644 --- a/Algorithmia/util.py +++ b/Algorithmia/util.py @@ -47,8 +47,3 @@ def md5_for_str(content): hash_md5 = hashlib.md5() hash_md5.update(content.encode()) return str(hash_md5.hexdigest()) - - -class ResponseWrapper: - def __init__(self, **entries): - self.__dict__.update(entries) \ No newline at end of file diff --git a/Test/regular/client_test.py b/Test/regular/client_test.py index a4e8730..7b3e4d5 100644 --- a/Test/regular/client_test.py +++ b/Test/regular/client_test.py @@ -215,7 +215,7 @@ def setUp(self): self.admin_username = self.admin_username + str(int(random() * 10000)) self.admin_org_name = self.admin_org_name + str(int(random() * 10000)) - self.admin_client = Algorithmia.client(api_address="https://test.algorithmia.com", + self.admin_client = Algorithmia.client(api_address="https://api.algorithmia.com", api_key=self.admin_api_key) self.regular_client = Algorithmia.client(api_address='https://api.algorithmia.com', api_key=self.regular_api_key) From bb6a335ff3ed3807e660d67f345851093a9895a9 Mon Sep 17 00:00:00 2001 From: zeryx <1892175+zeryx@users.noreply.github.com> Date: Wed, 11 May 2022 16:21:16 -0300 Subject: [PATCH 04/10] forgot to remove a dependency, and self signed cert branch wasn't updated --- Algorithmia/client.py | 2 +- Test/api/self_signed_app.py | 86 ++++++++++++++++++------------------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/Algorithmia/client.py b/Algorithmia/client.py index 97fd24e..0881a3a 100644 --- a/Algorithmia/client.py +++ b/Algorithmia/client.py @@ -7,7 +7,7 @@ from Algorithmia.datafile import DataFile, LocalDataFile, AdvancedDataFile from Algorithmia.datadirectory import DataDirectory, LocalDataDirectory, AdvancedDataDirectory from algorithmia_api_client import Configuration, DefaultApi, ApiClient -from Algorithmia.util import md5_for_file, md5_for_str, ResponseWrapper +from Algorithmia.util import md5_for_file, md5_for_str from tempfile import mkstemp import atexit import json, re, requests, six, certifi diff --git a/Test/api/self_signed_app.py b/Test/api/self_signed_app.py index 75ff3cc..693d486 100644 --- a/Test/api/self_signed_app.py +++ b/Test/api/self_signed_app.py @@ -80,7 +80,8 @@ async def process_get_algo(request: Request, username, algoname): "self_link": "https://api.algorithmia.com/v1/algorithms/quality/echo/versions/0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", "resource_type": "algorithm"} else: - return {"error":"No such algorithm"} + return {"error": "No such algorithm"} + @self_signed_app.get("/v1/algorithms/{username}/{algoname}/builds/{buildid}") async def get_build_id(username, algoname, buildid): @@ -93,6 +94,7 @@ async def get_build_id(username, algoname, buildid): async def get_build_log(username, algoname, buildid): return {"logs": "This is a log"} + @self_signed_app.get("/v1/algorithms/{username}/{algoname}/scm/status") async def get_scm_status(username, algoname): return {"scm_connection_status": "active"} @@ -174,50 +176,47 @@ async def publish_algorithm(request: Request, username, algoname): "resource_type": "algorithm"} +@self_signed_app.get("/v1/algorithms/{username}/{algoname}/versions") +async def versions_of_algorithm(request: Request, username, algoname): + return {"marker": None, "next_link": None, "results": [ + {"id": "21df7a38-eab8-4ac8-954c-41a285535e69", "name": algoname, + "details": {"summary": "", "label": algoname, "tagline": ""}, + "settings": {"algorithm_callability": "public", "source_visibility": "closed", "package_set": "python36", + "license": "apl", "royalty_microcredits": 0, "network_access": "full", "pipeline_enabled": True, + "insights_enabled": False, "algorithm_environment": "067110e7-8969-4441-b3d6-5333f18a3db3"}, + "version_info": {"semantic_version": "0.1.0", "git_hash": "0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", + "version_uuid": "e06d2808-bb5e-46ae-b7bc-f3d9968e3c6b"}, + "build": {"build_id": "a9ae2c93-6f4e-42c0-ac54-baa4a66e53d3", "status": "succeeded", + "commit_sha": "0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", "started_at": "2022-05-08T22:43:09.050Z", + "finished_at": "2022-05-08T22:43:28.646Z", "version_info": {"semantic_version": "0.1.0"}, + "resource_type": "algorithm_build"}, + "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, + "compilation": {"successful": True}, + "self_link": f"https://api.algorithmia.com/v1/algorithms/{username}/{algoname}/versions" + "/0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", + "resource_type": "algorithm"}]} + + @self_signed_app.get("/v1/algorithms/{username}/{algoname}/versions/{algohash}") async def get_algorithm_info(username, algoname, algohash): - return { - "id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", - "name": algoname, - "details": { - "summary": "Example Summary", - "label": "QA", - "tagline": "Example Tagline" - }, - "settings": { - "algorithm_callability": "private", - "source_visibility": "open", - "language": "python3", - "environment": "gpu", - "package_set": "tensorflow-gpu-2.3-python38", - "license": "apl", - "network_access": "isolated", - "pipeline_enabled": False, - "insights_enabled": False, - "algorithm_environment": "fd980f4f-1f1c-4b2f-a128-d60b40c6567a" - }, - "version_info": { - "semantic_version": "0.1.0", - "git_hash": algohash, - "release_notes": "created programmatically", - "sample_input": "\"payload\"", - "sample_output": "Exception encountered while running sample input", - "version_uuid": "1d9cb91d-11ca-49cb-a7f4-28f67f277654" - }, - "source": { - "scm": { - "id": "internal", - "provider": "internal", - "default": True, - "enabled": True - } - }, - "compilation": { - "successful": True, - "output": "" - }, - "resource_type": "algorithm" - } + if algohash == "e85db9bca2fad519f540b445f30d12523e4dec9c": + return {"id": "21df7a38-eab8-4ac8-954c-41a285535e69", "name": algoname, + "details": {"summary": "", "label": algoname, "tagline": ""}, + "settings": {"algorithm_callability": "public", "source_visibility": "closed", "language": "python3", + "environment": "cpu", "package_set": "python36", "license": "apl", + "royalty_microcredits": 0, "network_access": "full", "pipeline_enabled": True, + "insights_enabled": False, + "algorithm_environment": "067110e7-8969-4441-b3d6-5333f18a3db3"}, + "version_info": {"semantic_version": "0.1.0", "git_hash": "0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", + "version_uuid": "e06d2808-bb5e-46ae-b7bc-f3d9968e3c6b"}, + "build": {"build_id": "a9ae2c93-6f4e-42c0-ac54-baa4a66e53d3", "status": "succeeded", + "commit_sha": "0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", + "started_at": "2022-05-08T22:43:09.050Z", "finished_at": "2022-05-08T22:43:28.646Z", + "version_info": {"semantic_version": "0.1.0"}, "resource_type": "algorithm_build"}, + "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, + "compilation": {"successful": True, "output": ""}, "resource_type": "algorithm"} + else: + return {"error": {"message": "not found"}} ### Admin Routes @@ -309,6 +308,7 @@ async def get_org_by_name(org_name): "self_link": "http://localhost:8080/v1/organizations/a_myOrg1542" } + @self_signed_app.get("/v1/algorithms/{username}/{algoname}/builds/{buildid}/logs") async def get_build_log(username, algoname, buildid): return {"logs": "This is a log"} From 7140667b0010a092f63485891bb1f4743ecea717 Mon Sep 17 00:00:00 2001 From: zeryx <1892175+zeryx@users.noreply.github.com> Date: Wed, 11 May 2022 17:49:28 -0300 Subject: [PATCH 05/10] replaced all create/publish/update endpoints to use fixed urls and built-in requests package --- Algorithmia/algorithm.py | 70 +++++++++++++------------------------ Algorithmia/client.py | 20 +++++++---- Test/regular/client_test.py | 2 +- 3 files changed, 40 insertions(+), 52 deletions(-) diff --git a/Algorithmia/algorithm.py b/Algorithmia/algorithm.py index f7fd6b5..1ab867e 100644 --- a/Algorithmia/algorithm.py +++ b/Algorithmia/algorithm.py @@ -39,49 +39,29 @@ def set_options(self, timeout=300, stdout=False, output=OutputType.default, **qu return self # Create a new algorithm - def create(self, details={}, settings={}, version_info={}): - detailsObj = Details(**details) - settingsObj = SettingsMandatory(**settings) - createRequestVersionInfoObj = CreateRequestVersionInfo(**version_info) - create_parameters = {"name": self.algoname, "details": detailsObj, "settings": settingsObj, - "version_info": createRequestVersionInfoObj} - create_request = CreateRequest(**create_parameters) - try: - # Create Algorithm - api_response = self.client.manageApi.create_algorithm(self.username, create_request) - return api_response - except ApiException as e: - error_message = json.loads(e.body) - raise raiseAlgoApiError(error_message) + def create(self, details={}, settings={}, version_info={}, source={}, scmsCredentials={}): + url = "/v1/algorithms/" + self.username + create_parameters = {"name": self.algoname, "details": details, "settings": settings, + "version_info": version_info, "source": source, "scmsCredentials": scmsCredentials} + + api_response = self.client.postJsonHelper(url, create_parameters, parse_response_as_json=True) + return api_response # Update the settings in an algorithm - def update(self, details={}, settings={}, version_info={}): - detailsObj = Details(**details) - settingsObj = Settings(**settings) - createRequestVersionInfoObj = CreateRequestVersionInfo(**version_info) - update_parameters = {"details": detailsObj, "settings": settingsObj, - "version_info": createRequestVersionInfoObj} - update_request = UpdateRequest(**update_parameters) - try: - # Update Algorithm - api_response = self.client.manageApi.update_algorithm(self.username, self.algoname, update_request) - return api_response - except ApiException as e: - error_message = json.loads(e.body) - raise raiseAlgoApiError(error_message) + def update(self, details={}, settings={}, version_info={}, source={}, scmsCredentials={}): + url = "/v1/algorithms/" + self.username + "/" + self.algoname + update_parameters = {"details": details, "settings": settings, + "version_info": version_info, "source": source, "scmsCredentials": scmsCredentials} + api_response = self.client.putHelper(url, update_parameters) + return api_response # Publish an algorithm - def publish(self, details={}, settings={}, version_info={}): - try: - publish_parameters = {"details": details, "settings": settings, "version_info": version_info} - url = "/v1/algorithms/" + self.username + "/" + self.algoname + "/versions" - print(publish_parameters) - api_response = self.client.postJsonHelper(url, publish_parameters, parse_response_as_json=True, - **self.query_parameters) - return api_response - except ApiException as e: - error_message = json.loads(e.body) - raise raiseAlgoApiError(error_message) + def publish(self, details={}, settings={}, version_info={}, source={}, scmsCredentials={}): + url = "/v1/algorithms/" + self.username + "/" + self.algoname + "/versions" + publish_parameters = {"details": details, "settings": settings, + "version_info": version_info, "source": source, "scmsCredentials": scmsCredentials} + api_response = self.client.postJsonHelper(url, publish_parameters, parse_response_as_json=True) + return api_response def builds(self, limit=56, marker=None): kwargs = {"limit": limit, "marker": marker} @@ -93,18 +73,18 @@ def get_build(self, build_id): # Get the build object for a given build_id # The build status can have one of the following value: succeeded, failed, in-progress url = '/v1/algorithms/' + self.username + '/' + self.algoname + '/builds/' + build_id - response = self.client.getHelperAsJson(url) + response = self.client.getJsonHelper(url) return response def get_build_logs(self, build_id): # Get the algorithm build logs for a given build_id url = '/v1/algorithms/' + self.username + '/' + self.algoname + '/builds/' + build_id + '/logs' - response = self.client.getHelperAsJson(url) + response = self.client.getJsonHelper(url) return response def get_scm_status(self): url = '/v1/algorithms/' + self.username + '/' + self.algoname + '/scm/status' - response = self.client.getHelperAsJson(url) + response = self.client.getJsonHelper(url) return response # Get info on an algorithm @@ -114,14 +94,14 @@ def info(self, algo_hash=None): url = '/v1/algorithms/' + self.username + '/' + self.algoname + '/versions/' + algo_hash else: url = '/v1/algorithms/' + self.username + '/' + self.algoname + '/versions' - response = self.client.getHelperAsJson(url) + response = self.client.getJsonHelper(url) return response # Check if an Algorithm exists def exists(self): try: url = '/v1/algorithms/' + self.username + '/' + self.algoname - _ = self.client.getHelperAsJson(url) + _ = self.client.getJsonHelper(url) return True except AlgorithmException as e: print(e) @@ -143,7 +123,7 @@ def versions(self, limit=None, marker=None, published=None, callable=None): kwargs["callable"] = str(c).lower() if str(c) in bools else c # Get Algorithm versions url = '/v1/algorithms/' + self.username + '/' + self.algoname + '/versions' - response = self.client.getHelperAsJson(url) + response = self.client.getJsonHelper(url) return response # Compile an algorithm diff --git a/Algorithmia/client.py b/Algorithmia/client.py index 0881a3a..509eaf1 100644 --- a/Algorithmia/client.py +++ b/Algorithmia/client.py @@ -245,10 +245,17 @@ def postJsonHelper(self, url, input_object, parse_response_as_json=True, **query response = self.requestSession.post(self.apiAddress + url, data=input_json, headers=headers, params=query_parameters) - - if parse_response_as_json and response.status_code == 200: - return response.json() - return response + if 200 <= response.status_code <= 299: + if parse_response_as_json: + response = response.json() + if 'error' in response: + raise raiseAlgoApiError(response) + else: + return response + else: + return response + else: + raise raiseAlgoApiError(response) # Used internally to http get a file def getHelper(self, url, **query_parameters): @@ -259,14 +266,14 @@ def getHelper(self, url, **query_parameters): headers['Authorization'] = 'Bearer ' + self.bearerToken return self.requestSession.get(self.apiAddress + url, headers=headers, params=query_parameters) - def getHelperAsJson(self, url, **query_parameters): + def getJsonHelper(self, url, **query_parameters): headers = {} if self.apiKey is not None: headers['Authorization'] = self.apiKey elif self.bearerToken is not None: headers['Authorization'] = 'Bearer ' + self.bearerToken response = self.requestSession.get(self.apiAddress + url, headers=headers, params=query_parameters) - if response.status_code == 200: + if 200 <= response.status_code <= 299: response = response.json() if 'error' in response: raise raiseAlgoApiError(response) @@ -275,6 +282,7 @@ def getHelperAsJson(self, url, **query_parameters): else: raise raiseAlgoApiError(response) + def getStreamHelper(self, url, **query_parameters): headers = {} if self.apiKey is not None: diff --git a/Test/regular/client_test.py b/Test/regular/client_test.py index 7b3e4d5..8ff114c 100644 --- a/Test/regular/client_test.py +++ b/Test/regular/client_test.py @@ -147,7 +147,7 @@ def test_algorithm_programmatic_create_process(self): print("about to create algo") response = created_algo.create(details=details, settings=settings, version_info=version_info) print("created algo") - self.assertEqual(response.name, algorithm_name, "algorithm creation failed") + self.assertEqual(response['name'], algorithm_name, "algorithm creation failed") # --- Creation complete, compiling From 786502fc0424f518351666a2c575dda279ef9010 Mon Sep 17 00:00:00 2001 From: zeryx <1892175+zeryx@users.noreply.github.com> Date: Wed, 11 May 2022 18:09:00 -0300 Subject: [PATCH 06/10] simplified builds to follow the normal getJsonHelper pattern --- Algorithmia/CLI.py | 7 ++----- Algorithmia/algorithm.py | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Algorithmia/CLI.py b/Algorithmia/CLI.py index e0f7bab..c28399a 100644 --- a/Algorithmia/CLI.py +++ b/Algorithmia/CLI.py @@ -308,11 +308,8 @@ def list_languages(self, client): return table def getBuildLogs(self, user, algo, client): - try: - api_response = client.algo(user + '/' + algo).builds().json() - return json.dumps(api_response['results'], indent=1) - except AlgorithmException as e: - return json.dumps(e) + api_response = client.algo(user + '/' + algo).builds() + return json.dumps(api_response['results'], indent=1) def getconfigfile(self): diff --git a/Algorithmia/algorithm.py b/Algorithmia/algorithm.py index 1ab867e..b99e236 100644 --- a/Algorithmia/algorithm.py +++ b/Algorithmia/algorithm.py @@ -66,7 +66,7 @@ def publish(self, details={}, settings={}, version_info={}, source={}, scmsCrede def builds(self, limit=56, marker=None): kwargs = {"limit": limit, "marker": marker} url = "/v1/algorithms/" + self.username + "/" + self.algoname + '/builds' - response = self.client.getHelper(url, **kwargs) + response = self.client.getJsonHelper(url, **kwargs) return response def get_build(self, build_id): From 7271f34f00d2bddda60a06ddca0ab2f7a23e7aff Mon Sep 17 00:00:00 2001 From: zeryx <1892175+zeryx@users.noreply.github.com> Date: Wed, 11 May 2022 18:10:28 -0300 Subject: [PATCH 07/10] renamed builds to get_builds --- Algorithmia/CLI.py | 2 +- Algorithmia/algorithm.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Algorithmia/CLI.py b/Algorithmia/CLI.py index c28399a..565f5d0 100644 --- a/Algorithmia/CLI.py +++ b/Algorithmia/CLI.py @@ -308,7 +308,7 @@ def list_languages(self, client): return table def getBuildLogs(self, user, algo, client): - api_response = client.algo(user + '/' + algo).builds() + api_response = client.algo(user + '/' + algo).get_builds() return json.dumps(api_response['results'], indent=1) diff --git a/Algorithmia/algorithm.py b/Algorithmia/algorithm.py index b99e236..5d3c778 100644 --- a/Algorithmia/algorithm.py +++ b/Algorithmia/algorithm.py @@ -63,7 +63,7 @@ def publish(self, details={}, settings={}, version_info={}, source={}, scmsCrede api_response = self.client.postJsonHelper(url, publish_parameters, parse_response_as_json=True) return api_response - def builds(self, limit=56, marker=None): + def get_builds(self, limit=56, marker=None): kwargs = {"limit": limit, "marker": marker} url = "/v1/algorithms/" + self.username + "/" + self.algoname + '/builds' response = self.client.getJsonHelper(url, **kwargs) From 0f91135d94ee9a21a222a8c9f88978ed7bbb4ae7 Mon Sep 17 00:00:00 2001 From: zeryx <1892175+zeryx@users.noreply.github.com> Date: Wed, 11 May 2022 23:27:49 -0300 Subject: [PATCH 08/10] added an update endpoint test --- Test/api/app.py | 41 +++++++++++++++++++ Test/regular/algo_test.py | 82 +++++++++++++++++++++++++++++++++++++ Test/regular/client_test.py | 61 --------------------------- 3 files changed, 123 insertions(+), 61 deletions(-) diff --git a/Test/api/app.py b/Test/api/app.py index 19c4b6a..e502272 100644 --- a/Test/api/app.py +++ b/Test/api/app.py @@ -116,6 +116,47 @@ async def create_algorithm(request: Request, username): "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, "resource_type": "algorithm"} +@regular_app.put('/v1/algorithms/{username}/{algoname}') +async def update_algorithm(request: Request, username, algoname): + payload = await request.json() + return { + "id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", + "name": algoname, + "details": { + "summary": "Example Summary", + "label": "QA", + "tagline": "Example Tagline" + }, + "settings": { + "algorithm_callability": "private", + "source_visibility": "open", + "package_set": "tensorflow-gpu-2.3-python38", + "license": "apl", + "network_access": "isolated", + "pipeline_enabled": False, + "insights_enabled": False, + "algorithm_environment": "fd980f4f-1f1c-4b2f-a128-d60b40c6567a" + }, + "version_info": { + "git_hash": "e85db9bca2fad519f540b445f30d12523e4dec9c", + "version_uuid": "1d9cb91d-11ca-49cb-a7f4-28f67f277654" + }, + "source": { + "scm": { + "id": "internal", + "provider": "internal", + "default": True, + "enabled": True + } + }, + "compilation": { + "successful": True, + "output": "" + }, + "self_link": f"http://localhost:8080/v1/algorithms/{username}/{algoname}/versions/e85db9bca2fad519f540b445f30d12523e4dec9c", + "resource_type": "algorithm" + } + @regular_app.post("/v1/algorithms/{username}/{algoname}/compile") async def compile_algorithm(username, algoname): diff --git a/Test/regular/algo_test.py b/Test/regular/algo_test.py index 068f40f..b1da4af 100644 --- a/Test/regular/algo_test.py +++ b/Test/regular/algo_test.py @@ -16,6 +16,7 @@ class AlgoDummyTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.client = Algorithmia.client(api_address="http://localhost:8080", api_key="simabcd123") + cls.environment_id = "abcd-123" def test_call_customCert(self): result = self.client.algo('quality/echo').pipe(bytearray('foo', 'utf-8')) @@ -75,6 +76,26 @@ def test_algo_info(self): self.assertTrue('resource_type' in result['results'][0]) self.assertTrue(result['results'][0]['resource_type'] == "algorithm") + def test_update_algo(self): + details = { + "summary": "Example Summary", + "label": "QA", + "tagline": "Example Tagline" + } + settings = { + "source_visibility": "open", + "algorithm_environment": self.environment_id, + "license": "apl", + "network_access": "isolated", + "pipeline_enabled": False + } + version_info = { + "sample_input": "hello" + } + result = self.client.algo('quality/echo').update(details=details, settings=settings, version_info=version_info) + self.assertTrue('id' in result) + + def test_get_build_by_id(self): result = self.client.algo("quality/echo").get_build("1a392e2c-b09f-4bae-a616-56c0830ac8e5") self.assertTrue('commit_sha' in result) @@ -93,6 +114,67 @@ def test_exception_ipa_algo(self): except AlgorithmException as e: self.assertEqual(e.message, "This is an exception") + def test_algorithm_programmatic_create_process(self): + algorithm_name = "hello" + payload = "John" + expected_response = "hello John" + full_path = "quality/" + algorithm_name + details = { + "summary": "Example Summary", + "label": "QA", + "tagline": "Example Tagline" + } + settings = { + "source_visibility": "open", + "algorithm_environment": self.environment_id, + "license": "apl", + "network_access": "isolated", + "pipeline_enabled": False + } + version_info = { + "sample_input": "hello" + } + created_algo = self.client.algo(full_path) + print("about to create algo") + response = created_algo.create(details=details, settings=settings, version_info=version_info) + print("created algo") + self.assertEqual(response['name'], algorithm_name, "algorithm creation failed") + + # --- Creation complete, compiling + + response = created_algo.compile() + git_hash = response['version_info']['git_hash'] + algo_with_build = self.client.algo(full_path + "/" + git_hash) + self.assertEqual(response['name'], created_algo.algoname) + + # --- compiling complete, now testing algorithm request + response = algo_with_build.pipe(payload).result + self.assertEqual(response, expected_response, "compiling failed") + + # --- testing complete, now publishing new release. + + pub_settings = {"algorithm_callability": "private"} + pub_version_info = { + "release_notes": "created programmatically", + "sample_input": payload, + "version_type": "minor" + } + pub_details = {"label": "testing123"} + + response = algo_with_build.publish( + details=pub_details, + settings=pub_settings, + version_info=pub_version_info + ) + self.assertEqual(response["version_info"]["semantic_version"], "0.1.0", + "Publishing failed, semantic version is not correct.") + + # --- publishing complete, getting additional information + + response = created_algo.info(git_hash) + + self.assertEqual(response['version_info']['semantic_version'], "0.1.0", "information is incorrect") + else: class AlgoTest(unittest.TestCase): def setUp(self): diff --git a/Test/regular/client_test.py b/Test/regular/client_test.py index 8ff114c..b0e821b 100644 --- a/Test/regular/client_test.py +++ b/Test/regular/client_test.py @@ -28,7 +28,6 @@ def setUp(self): self.admin_username = self.admin_username + str(int(random() * 10000)) self.admin_org_name = self.admin_org_name + str(int(random() * 10000)) - self.environment_id = "abcd-123" def test_create_user(self): response = self.client.create_user( @@ -123,66 +122,6 @@ def test_get_algorithm_errors(self): else: self.assertEqual(404, response.status_code) - def test_algorithm_programmatic_create_process(self): - algorithm_name = "algo_e2d_test" - payload = "John" - expected_response = "hello John" - full_path = "a_Mrtest/" + algorithm_name - details = { - "summary": "Example Summary", - "label": "QA", - "tagline": "Example Tagline" - } - settings = { - "source_visibility": "open", - "algorithm_environment": self.environment_id, - "license": "apl", - "network_access": "isolated", - "pipeline_enabled": False - } - version_info = { - "sample_input": "hello" - } - created_algo = self.client.algo(full_path) - print("about to create algo") - response = created_algo.create(details=details, settings=settings, version_info=version_info) - print("created algo") - self.assertEqual(response['name'], algorithm_name, "algorithm creation failed") - - # --- Creation complete, compiling - - response = created_algo.compile() - git_hash = response['version_info']['git_hash'] - algo_with_build = self.client.algo(full_path + "/" + git_hash) - self.assertEqual(response['name'], created_algo.algoname) - - # --- compiling complete, now testing algorithm request - response = algo_with_build.pipe(payload).result - self.assertEqual(response, expected_response, "compiling failed") - - # --- testing complete, now publishing new release. - - pub_settings = {"algorithm_callability": "private"} - pub_version_info = { - "release_notes": "created programmatically", - "sample_input": payload, - "version_type": "minor" - } - pub_details = {"label": "testing123"} - - response = algo_with_build.publish( - details=pub_details, - settings=pub_settings, - version_info=pub_version_info - ) - self.assertEqual(response["version_info"]["semantic_version"], "0.1.0", - "Publishing failed, semantic version is not correct.") - - # --- publishing complete, getting additional information - - response = created_algo.info(git_hash) - - self.assertEqual(response['version_info']['semantic_version'], "0.1.0", "information is incorrect") def test_no_auth_client(self): From 1f668f6865eb61bb5b1d3401c7c05a17cdf97ba9 Mon Sep 17 00:00:00 2001 From: zeryx <1892175+zeryx@users.noreply.github.com> Date: Wed, 11 May 2022 23:41:20 -0300 Subject: [PATCH 09/10] fixed the put operation; follows the other helper endpoint systems --- Algorithmia/client.py | 10 ++++++++-- Test/api/app.py | 1 - 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Algorithmia/client.py b/Algorithmia/client.py index 509eaf1..30dec03 100644 --- a/Algorithmia/client.py +++ b/Algorithmia/client.py @@ -317,11 +317,17 @@ def putHelper(self, url, data): headers['Authorization'] = 'Bearer ' + self.bearerToken if isJson(data): headers['Content-Type'] = 'application/json' - response = self.requestSession.put(self.apiAddress + url, data=data, headers=headers) if response._content == b'': return response - return response.json() + if 200 <= response.status_code <= 299: + response = response.json() + if 'error' in response: + raise raiseAlgoApiError(response) + else: + return response + else: + raise raiseAlgoApiError(response) # Used internally to http delete a file def deleteHelper(self, url): diff --git a/Test/api/app.py b/Test/api/app.py index e502272..cd80621 100644 --- a/Test/api/app.py +++ b/Test/api/app.py @@ -118,7 +118,6 @@ async def create_algorithm(request: Request, username): @regular_app.put('/v1/algorithms/{username}/{algoname}') async def update_algorithm(request: Request, username, algoname): - payload = await request.json() return { "id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", "name": algoname, From 137db110a865e6f4a92931ab19b1246a660e8558 Mon Sep 17 00:00:00 2001 From: zeryx <1892175+zeryx@users.noreply.github.com> Date: Wed, 11 May 2022 23:54:26 -0300 Subject: [PATCH 10/10] removed mutating and cluster specific test cases from client tests (python 2) --- Test/regular/client_test.py | 135 ------------------------------------ 1 file changed, 135 deletions(-) diff --git a/Test/regular/client_test.py b/Test/regular/client_test.py index b0e821b..3a0e1be 100644 --- a/Test/regular/client_test.py +++ b/Test/regular/client_test.py @@ -138,140 +138,5 @@ def test_no_auth_client(self): finally: os.environ['ALGORITHMIA_API_KEY'] = key self.assertEqual(str(error), str(AlgorithmException(message="authorization required", stack_trace=None, error_type=None))) - -else: - class ClientTest(unittest.TestCase): - seed(datetime.now().microsecond) - # due to legacy reasons, regular client tests are tested against api.algorithmia.com, whereas admin api tests - # are run against test.algorithmia.com. - admin_username = "a_Mrtest" - admin_org_name = "a_myOrg" - environment_name = "Python 3.9" - - def setUp(self): - self.admin_api_key = unicode(os.environ.get('ALGORITHMIA_A_KEY')) - self.regular_api_key = unicode(os.environ.get('ALGORITHMIA_API_KEY')) - - self.admin_username = self.admin_username + str(int(random() * 10000)) - self.admin_org_name = self.admin_org_name + str(int(random() * 10000)) - self.admin_client = Algorithmia.client(api_address="https://api.algorithmia.com", - api_key=self.admin_api_key) - self.regular_client = Algorithmia.client(api_address='https://api.algorithmia.com', - api_key=self.regular_api_key) - - environments = self.regular_client.get_environment("python3") - for environment in environments['environments']: - if environment['display_name'] == self.environment_name: - self.environment_id = environment['id'] - - def test_create_user(self): - response = self.admin_client.create_user( - {"username": self.admin_username, "email": self.admin_username + "@algo.com", "passwordHash": "", - "shouldCreateHello": False}) - - if type(response) is dict: - self.assertEqual(self.admin_username, response['username']) - else: - self.assertIsNotNone(response) - - def test_get_org_types(self): - response = self.admin_client.get_org_types() - self.assertTrue(len(response) > 0) - - def test_create_org(self): - response = self.admin_client.create_org( - {"org_name": self.admin_org_name, "org_label": "some label", "org_contact_name": "Some owner", - "org_email": self.admin_org_name + "@algo.com", "type_id": "basic"}) - - self.assertEqual(self.admin_org_name, response[u'org_name']) - - def test_get_org(self): - response = self.admin_client.get_org("a_myOrg84") - self.assertEqual("a_myOrg84", response['org_name']) - - def test_get_environment(self): - response = self.admin_client.get_environment("python2") - - if u'error' not in response: - self.assertTrue(response is not None and u'environments' in response) - - def test_edit_org(self): - org_name = "a_myOrg84" - - obj = { - "id": "b85d8c4e-7f3c-40b9-9659-6adc2cb0e16f", - "org_name": "a_myOrg84", - "org_label": "some label", - "org_contact_name": "Some owner", - "org_email": "a_myOrg84@algo.com", - "org_created_at": "2020-11-30T23:51:40", - "org_url": "https://algorithmia.com", - "type_id": "basic", - "resource_type": "organization" - } - - response = self.admin_client.edit_org(org_name, obj) - if type(response) is dict: - print(response) - else: - self.assertEqual(204, response.status_code) - - def test_get_template(self): - filename = "./temptest" - response = self.admin_client.get_template("36fd467e-fbfe-4ea6-aa66-df3f403b7132", filename) - - if type(response) is dict: - self.assertTrue(u'error' in response or u'message' in response) - else: - self.assertTrue(response.ok) - try: - shutil.rmtree(filename) - except OSError as e: - print(e) - - def test_get_supported_languages(self): - response = self.admin_client.get_supported_languages() - self.assertTrue(response is not None) - - if type(response) is not list: - self.assertTrue(u'error' in response) - else: - language_found = any('anaconda3' in languages['name'] for languages in response) - self.assertTrue(response is not None and language_found) - - def test_invite_to_org(self): - response = self.admin_client.invite_to_org("a_myOrg38", "a_Mrtest4") - if type(response) is dict: - self.assertTrue(u'error' in response) - else: - self.assertEqual(200, response.status_code) - - # This test will require updating after the /v1/organizations/{org_name}/errors endpoint has been - # deployed to the remote environment. - def test_get_organization_errors(self): - response = self.admin_client.get_organization_errors(self.admin_org_name) - self.assertTrue(response is not None) - - if type(response) is list: - self.assertEqual(0, len(response), 'Received unexpected result, should have been 0.') - - def test_get_user_errors(self): - response = self.admin_client.get_user_errors(self.admin_username) - - self.assertTrue(response is not None) - self.assertEqual(0, len(response)) - - def test_get_algorithm_errors(self): - response = self.admin_client.get_algorithm_errors('hello') - self.assertTrue(response is not None) - - if type(response) is dict: - self.assertTrue(u'error' in response) - else: - self.assertEqual(404, response.status_code) - - def test_algo_freeze(self): - self.regular_client.freeze("Test/resources/manifests/example_manifest.json", "Test/resources/manifests") - if __name__ == '__main__': unittest.main()