diff --git a/Algorithmia/CLI.py b/Algorithmia/CLI.py index 3acc6ae..565f5d0 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,12 +308,10 @@ def list_languages(self, client): return table def getBuildLogs(self, user, algo, client): - api_response = client.algo(user + '/' + algo).build_logs() - - if "error" in api_response: - return json.dumps(api_response) + api_response = client.algo(user + '/' + algo).get_builds() return json.dumps(api_response['results'], indent=1) + def getconfigfile(self): if (os.name == "posix"): # if!windows diff --git a/Algorithmia/algorithm.py b/Algorithmia/algorithm.py index 378e1c0..5d3c778 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, \ @@ -40,105 +39,73 @@ 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={}): - publish_parameters = {"details": details, "settings": settings, "version_info": version_info} + def publish(self, details={}, settings={}, version_info={}, source={}, scmsCredentials={}): 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) + 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 - # 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) - return api_response - except ApiException as e: - error_message = json.loads(e.body) - raise raiseAlgoApiError(error_message) + 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) + 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.getJsonHelper(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.getJsonHelper(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.getJsonHelper(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.getJsonHelper(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.getJsonHelper(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 +121,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.getJsonHelper(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..30dec03 100644 --- a/Algorithmia/client.py +++ b/Algorithmia/client.py @@ -2,6 +2,7 @@ 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 @@ -15,6 +16,7 @@ from time import time + class Client(object): 'Algorithmia Common Library' @@ -243,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): @@ -257,6 +266,23 @@ def getHelper(self, url, **query_parameters): headers['Authorization'] = 'Bearer ' + self.bearerToken return self.requestSession.get(self.apiAddress + url, headers=headers, params=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 200 <= response.status_code <= 299: + 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: @@ -291,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 99c192b..cd80621 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"} @@ -93,9 +116,8 @@ async def create_algorithm(request: Request, username): "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, "resource_type": "algorithm"} - -@regular_app.post("/v1/algorithms/{username}/{algoname}/compile") -async def compile_algorithm(username, algoname): +@regular_app.put('/v1/algorithms/{username}/{algoname}') +async def update_algorithm(request: Request, username, algoname): return { "id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", "name": algoname, @@ -135,25 +157,8 @@ async def compile_algorithm(username, algoname): } -@regular_app.post("/v1/algorithms/{username}/{algoname}/versions") -async def publish_algorithm(request: Request, username, algoname): - 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": {"semantic_version": "0.1.0", "git_hash": "e85db9bca2fad519f540b445f30d12523e4dec9c", - "release_notes": "created programmatically", "sample_input": "payload", - "version_uuid": "e85db9bca2fad519f540b445f30d12523e4dec9c"}, - "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, - "compilation": {"successful": True}, - "self_link": f"http://localhost:8080/v1/algorithms/%7Busername%7D/%7Balgoname%7D/versions/e85db9bca2fad519f540b445f30d12523e4dec9c", - "resource_type": "algorithm"} - - -@regular_app.get("/v1/algorithms/{username}/{algoname}/versions/{algohash}") -async def get_algorithm_info(username, algoname, algohash): +@regular_app.post("/v1/algorithms/{username}/{algoname}/compile") +async def compile_algorithm(username, algoname): return { "id": "2938ca9f-54c8-48cd-b0d0-0fb7f2255cdc", "name": algoname, @@ -165,8 +170,6 @@ async def get_algorithm_info(username, algoname, algohash): "settings": { "algorithm_callability": "private", "source_visibility": "open", - "language": "python3", - "environment": "gpu", "package_set": "tensorflow-gpu-2.3-python38", "license": "apl", "network_access": "isolated", @@ -175,11 +178,7 @@ async def get_algorithm_info(username, algoname, algohash): "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", + "git_hash": "e85db9bca2fad519f540b445f30d12523e4dec9c", "version_uuid": "1d9cb91d-11ca-49cb-a7f4-28f67f277654" }, "source": { @@ -194,10 +193,71 @@ async def get_algorithm_info(username, algoname, algohash): "successful": True, "output": "" }, + "self_link": f"http://localhost:8080/v1/algorithms/%7Busername%7D/%7Balgoname%7D/versions/e85db9bca2fad519f540b445f30d12523e4dec9c", "resource_type": "algorithm" } +@regular_app.post("/v1/algorithms/{username}/{algoname}/versions") +async def publish_algorithm(request: Request, username, algoname): + 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": {"semantic_version": "0.1.0", "git_hash": "e85db9bca2fad519f540b445f30d12523e4dec9c", + "release_notes": "created programmatically", "sample_input": "payload", + "version_uuid": "e85db9bca2fad519f540b445f30d12523e4dec9c"}, + "source": {"scm": {"id": "internal", "provider": "internal", "default": True, "enabled": True}}, + "compilation": {"successful": True}, + "self_link": f"http://localhost:8080/v1/algorithms/%7Busername%7D/%7Balgoname%7D/versions/e85db9bca2fad519f540b445f30d12523e4dec9c", + "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/%7Busername%7D/%7Balgoname%7D/versions" + "/0cfd7a6600f1fa05f9fe93016e661a9332c4ded2", + "resource_type": "algorithm"}]} + + +@regular_app.get("/v1/algorithms/{username}/{algoname}/versions/{algohash}") +async def get_algorithm_info(username, algoname, algohash): + 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 @regular_app.post("/v1/users") async def create_user(request: Request): @@ -287,6 +347,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 +411,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..693d486 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,29 @@ 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", @@ -70,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"} @@ -151,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/%7Busername%7D/%7Balgoname%7D/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 @@ -286,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"} diff --git a/Test/regular/algo_test.py b/Test/regular/algo_test.py index 0e3afdd..b1da4af 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,101 @@ 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") + cls.environment_id = "abcd-123" 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_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("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: @@ -71,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): @@ -79,7 +183,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 +192,44 @@ 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) + #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_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..3a0e1be 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( @@ -61,17 +60,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" @@ -134,61 +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 - } - 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): @@ -205,207 +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://test.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() 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..b3b377f 100644 --- a/Test/self_signed/algo_test.py +++ b/Test/self_signed/algo_test.py @@ -3,128 +3,89 @@ from Algorithmia.errors import AlgorithmException 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): @classmethod 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_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_algo_exists(self): + result = self.client.algo('quality/echo').exists() + self.assertEquals(True, 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_algo_no_exists(self): + result = self.client.algo('quality/not_echo').exists() + self.assertEquals(False, 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) - - 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) + # 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") - 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: 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()