From a4e21c2de0305a4ef64616bdb8a397a75a13255a Mon Sep 17 00:00:00 2001 From: Sakari Rautiainen Date: Wed, 2 Jan 2019 14:57:25 -0800 Subject: [PATCH 1/3] Revert "add support for access groups" --- testdroid/__init__.py | 167 ++++++------------------------------------ 1 file changed, 21 insertions(+), 146 deletions(-) diff --git a/testdroid/__init__.py b/testdroid/__init__.py index f36f1eb..4441d77 100755 --- a/testdroid/__init__.py +++ b/testdroid/__init__.py @@ -32,10 +32,10 @@ def __init__(self, msg): class RequestResponseError(Exception): def __init__(self, msg, status_code): - super(Exception, self).__init__("Request Error: code %s: %s" % + super(Exception, self).__init__("Request Error: code %s: %s" % (status_code, msg) ) self.status_code = status_code - + """ Format unix timestamp to human readable. Automatically detects timestamps with seconds or milliseconds. """ @@ -324,7 +324,7 @@ def print_available_free_android_devices(self, limit=0): if device['creditsPrice'] == 0 and device['locked'] == False and device['osType'] == "ANDROID": print(device['displayName']) print("") - + """ Print available frameworks """ def print_available_frameworks(self, os_type=None, limit=0): @@ -387,7 +387,7 @@ def get_project(self, project_id): def print_projects(self, limit=0): me = self.get_me() print("Projects for %s <%s>:" % (me['name'], me['email'])) - + for project in self.get_projects(limit)['data']: print("%s %s \"%s\"" % (str(project['id']).ljust(10), project['type'].ljust(15), project['name'])) @@ -468,9 +468,9 @@ def set_project_framework(self, project_id, frameworkId): """ Start a test run using test run config - e.g '{"frameworkId":12252, - "osType": "ANDROID", - "projectId":1234, + e.g '{"frameworkId":12252, + "osType": "ANDROID", + "projectId":1234, "files":[{"id":9876}, {"id":5432}] "testRunParameters":[{"key":"xyz", "value":"abc"}], "deviceGroupId":6854 @@ -629,7 +629,7 @@ def get_device_run_files(self, project_id, test_run_id, device_session_id, tags= else: return self.get("me/projects/%s/runs/%s/device-sessions/%s/output-file-set/files?tag[]=%s" % (project_id, test_run_id, device_session_id, tags)) - """ Get list of input files + """ Get list of input files """ def get_input_files(self, limit=0): return self.get("me/files?limit={}&filter=s_direction_eq_INPUT".format(limit)) @@ -650,8 +650,8 @@ def print_builds(self, job_id, limit=0): for build in self.get_builds(job_id, limit)['data']: print("%s %s %s %s %s" % (str(build['id']).ljust(12), str(build['buildNumber']).ljust(5), build['state'].ljust(10), build['status'].ljust(10), build['duration'])) - - + + """ Get builds from the job """ def get_builds(self, job_id, limit=0): @@ -692,14 +692,14 @@ def create_job(self, job_name, content, job_type="BUILD"): isDirectory fileUrlEnvVariable - usage: client.create_build(job_id, json.dumps({"fileId":123213...)) + usage: client.create_build(job_id, json.dumps({"fileId":123213...)) """ def create_build(self, job_id, build_config={}): build = self.post(path="me/jobs/{}/builds".format(job_id), payload=build_config, headers={'Content-type': 'application/json', 'Accept': 'application/json'}) logger.info("build %s: %s (%s) " % (build['id'], build['buildNumber'], build['state'] )) return build - """ Upload job + """ Update job """ def upload_job(self, job_id,job_name, content): job = self.post(path="me/jobs/{}".format(job_id), payload={"name": job_name, "content": content}) @@ -756,7 +756,7 @@ def wait_build(self, job_id, build_id): while True: time.sleep(self.polling_interval_mins * 6) if not self.api_key: - self.access_token = None + self.access_token = None self.get_token() buildStatus = self.get_build(job_id, build_id) if buildStatus and 'state' in buildStatus: @@ -857,91 +857,6 @@ def download_test_screenshots(self, project_id, test_run_id): else: logger.info("Device %s has errored or has not finished - skipping" % device_run['device']['displayName']) - """ Get access groups - """ - def get_access_groups(self): - return self.get("me/access-groups") - - """ Get access group by id - """ - def get_access_group(self, access_group_id): - return self.get("me/access-groups/{}".format(access_group_id)) - - """ Create access group - """ - def create_access_group(self, access_group_name, access_group_scope="USER"): - group = self.post(path="me/access-groups", payload={"name": access_group_name, "scope": access_group_scope}) - return group - - """ Update access group - """ - def update_access_group(self, access_group_id, access_group_name, access_group_scope): - # TODO: what if group_name or group_scope aren't provided?? - group = self.post(path="me/access-groups/{}".format(access_group_id), payload={"name": access_group_name, "scope": access_group_scope}) - return group - - """ Delete access group - """ - def delete_access_group(self, access_group_id): - # TODO: what if group_name or group_scope aren't provided?? - return self.delete(path="me/access-groups/{}".format(access_group_id)) - - """ Get access group resources by id - """ - def get_access_group_resources(self, access_group_id): - return self.get("me/access-groups/{}/resources".format(access_group_id)) - - """ Get resource from access group - """ - def get_access_group_resource(self, access_group_id, resource_id): - return self.get("ame/ccess-groups/{}/resources/{}".format(access_group_id, resource_id)) - - """ Delete resource from access group - """ - def delete_access_group_resource(self, access_group_id, resource_id): - return self.delete("me/access-groups/{}/resources/{}".format(access_group_id, resource_id)) - - """ Get access group users - """ - def get_access_group_users(self, access_group_id): - return self.get("me/access-groups/{}/users".format(access_group_id)) - - """ Add user to access group - """ - def add_access_group_user(self, access_group_id, email): - return self.post("me/access-groups/{}/users".format(access_group_id), payload={"email": email}) - - """ Get user from access group - """ - def get_access_group_user(self, access_group_id, user_id): - return self.get("me/access-groups/{}/users/{}".format(access_group_id, user_id)) - - """ Delete user from access group - """ - def delete_access_group_user(self, access_group_id, user_id): - return self.delete("me/access-groups/{}/users/{}".format(access_group_id, user_id)) - - """ Share device group with access group - """ - def share_device_group(self, device_group_id, access_group_id): - return self.post("me/device-groups/{}/share".format(device_group_id), payload={"accessGroupId": access_group_id}) - - """ Share file set with access group - """ - def share_file_set(self, file_set_id, access_group_id): - return self.post("me/file-sets/{}/share".format(file_set_id), payload={"accessGroupId": access_group_id}) - - """ Share file with access group - """ - def share_file(self, file_id, access_group_id): - return self.post("me/files/{}/share".format(file_id), payload={"accessGroupId": access_group_id}) - - """ Share project with access group - """ - def share_project(self, project_id, access_group_id): - return self.post("me/projects/{}/share".format(project_id), payload={"accessGroupId": access_group_id}) - - def get_parser(self): class MyParser(OptionParser): def format_epilog(self, formatter): @@ -990,11 +905,11 @@ def format_epilog(self, formatter): Download test run screenshots. Screenshots will be downloaded to current directory in a structure: [test-run-id]/[device-run-id]-[device-name]/screenshots/... - jobs Get list of your jobs - builds Get list of your builds + jobs Get list of your jobs + builds Get list of your builds create-job Create a new job. Job configuration in Jenkins pipeline format See the sample of Jenkisfile in http://docs.bitbar.com/build-service/guide.html - update-job + update-job Update existing job create-build Create a new build job. See https://cloud.testdroid.com/cloud/swagger-ui.html for details of build configuration @@ -1003,32 +918,6 @@ def format_epilog(self, formatter): download-builds-files Download all the results of the specific build wait-build Await completion (polling) of the build - access-groups Get access groups - access-group Get an access group by id - access-group-create Create a new access group - access-group-update - Update an access group - access-group-delete Delete an access group - access-group-resources Get resources in an access group - access-group-resource - Get a resource in an access group by id - access-group-resource-remove - Remove a resource from an access group - access-group-users Get users in an access group - access-group-users-get - Get a user in an access group - access-group-users-add - Add a user to an access group - access-group-users-remove - Remove a user from an access group - - share-device-group - Share a device group with an access group - share-file-set - Share a file set with an access group - share-file Share a file with an access group - share-project - Share a project with an access group """ parser = MyParser(usage=usage, description=description, epilog=epilog, version="%s %s" % ("%prog", __version__)) parser.add_option("-k", "--apikey", dest="apikey", @@ -1048,7 +937,7 @@ def format_epilog(self, formatter): return parser def get_commands(self): - return { + commands = { "me": self.get_me, "device-groups": self.print_device_groups, "available-free-devices": self.print_available_free_devices, @@ -1079,24 +968,10 @@ def get_commands(self): "delete-job": self.delete_job, "delete-build": self.delete_build, "download-builds-files": self.download_build_output_files, - "wait-build": self.wait_build, - "access-groups": self.get_access_groups, - "access-group": self.get_access_group, - "access-group-create": self.create_access_group, - "access-group-update": self.update_access_group, - "access-group-delete": self.delete_access_group, - "access-group-resources": self.get_access_group_resources, - "access-group-resource": self.get_access_group_resource, - "access-group-resource-remove": self.delete_access_group_resource, - "access-group-users": self.get_access_group_users, - "access-group-users-add": self.add_access_group_user, - "access-group-users-get": self.get_access_group_user, - "access-group-users-remove": self.delete_access_group_user, - "share-device-group": self.share_device_group, - "share-file-set": self.share_file_set, - "share-file": self.share_file, - "share-project": self.share_project, + "wait-build": self.wait_build + } + return commands def cli(self, parser, commands): (options, args) = parser.parse_args() @@ -1110,7 +985,7 @@ def cli(self, parser, commands): if sys.version_info[0] > 2: http.client.HTTPConnection.debuglevel = 1 else: - httplib.HTTPConnection.debuglevel = 1 + httplib.HTTPConnection.debuglevel = 1 logging.getLogger().setLevel(logging.DEBUG) requests_log = logging.getLogger("requests.packages.urllib3") requests_log.setLevel(logging.DEBUG) From f93ba9d17056d311673f08f9ff6b6111bb6762b5 Mon Sep 17 00:00:00 2001 From: Atte Keltanen Date: Fri, 21 Dec 2018 10:44:50 -0800 Subject: [PATCH 2/3] add support for access groups --- testdroid/__init__.py | 167 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 146 insertions(+), 21 deletions(-) diff --git a/testdroid/__init__.py b/testdroid/__init__.py index 45acc85..5a5275e 100755 --- a/testdroid/__init__.py +++ b/testdroid/__init__.py @@ -32,10 +32,10 @@ def __init__(self, msg): class RequestResponseError(Exception): def __init__(self, msg, status_code): - super(Exception, self).__init__("Request Error: code %s: %s" % + super(Exception, self).__init__("Request Error: code %s: %s" % (status_code, msg) ) self.status_code = status_code - + """ Format unix timestamp to human readable. Automatically detects timestamps with seconds or milliseconds. """ @@ -324,7 +324,7 @@ def print_available_free_android_devices(self, limit=0): if device['creditsPrice'] == 0 and device['locked'] == False and device['osType'] == "ANDROID": print(device['displayName']) print("") - + """ Print available frameworks """ def print_available_frameworks(self, os_type=None, limit=0): @@ -387,7 +387,7 @@ def get_project(self, project_id): def print_projects(self, limit=0): me = self.get_me() print("Projects for %s <%s>:" % (me['name'], me['email'])) - + for project in self.get_projects(limit)['data']: print("%s %s \"%s\"" % (str(project['id']).ljust(10), project['type'].ljust(15), project['name'])) @@ -468,9 +468,9 @@ def set_project_framework(self, project_id, frameworkId): """ Start a test run using test run config - e.g '{"frameworkId":12252, - "osType": "ANDROID", - "projectId":1234, + e.g '{"frameworkId":12252, + "osType": "ANDROID", + "projectId":1234, "files":[{"id":9876}, {"id":5432}] "testRunParameters":[{"key":"xyz", "value":"abc"}], "deviceGroupId":6854 @@ -629,7 +629,7 @@ def get_device_run_files(self, project_id, test_run_id, device_session_id, tags= else: return self.get("me/projects/%s/runs/%s/device-sessions/%s/output-file-set/files?tag[]=%s" % (project_id, test_run_id, device_session_id, tags)) - """ Get list of input files + """ Get list of input files """ def get_input_files(self, limit=0): return self.get("me/files?limit={}&filter=s_direction_eq_INPUT".format(limit)) @@ -650,8 +650,8 @@ def print_builds(self, job_id, limit=0): for build in self.get_builds(job_id, limit)['data']: print("%s %s %s %s %s" % (str(build['id']).ljust(12), str(build['buildNumber']).ljust(5), build['state'].ljust(10), build['status'].ljust(10), build['duration'])) - - + + """ Get builds from the job """ def get_builds(self, job_id, limit=0): @@ -692,14 +692,14 @@ def create_job(self, job_name, content, job_type="BUILD"): isDirectory fileUrlEnvVariable - usage: client.create_build(job_id, json.dumps({"fileId":123213...)) + usage: client.create_build(job_id, json.dumps({"fileId":123213...)) """ def create_build(self, job_id, build_config={}): build = self.post(path="me/jobs/{}/builds".format(job_id), payload=build_config, headers={'Content-type': 'application/json', 'Accept': 'application/json'}) logger.info("build %s: %s (%s) " % (build['id'], build['buildNumber'], build['state'] )) return build - """ Update job + """ Upload job """ def upload_job(self, job_id,job_name, content): job = self.post(path="me/jobs/{}".format(job_id), payload={"name": job_name, "content": content}) @@ -756,7 +756,7 @@ def wait_build(self, job_id, build_id): while True: time.sleep(self.polling_interval_mins * 6) if not self.api_key: - self.access_token = None + self.access_token = None self.get_token() buildStatus = self.get_build(job_id, build_id) if buildStatus and 'state' in buildStatus: @@ -857,6 +857,91 @@ def download_test_screenshots(self, project_id, test_run_id): else: logger.info("Device %s has errored or has not finished - skipping" % device_run['device']['displayName']) + """ Get access groups + """ + def get_access_groups(self): + return self.get("me/access-groups") + + """ Get access group by id + """ + def get_access_group(self, access_group_id): + return self.get("me/access-groups/{}".format(access_group_id)) + + """ Create access group + """ + def create_access_group(self, access_group_name, access_group_scope="USER"): + group = self.post(path="me/access-groups", payload={"name": access_group_name, "scope": access_group_scope}) + return group + + """ Update access group + """ + def update_access_group(self, access_group_id, access_group_name, access_group_scope): + # TODO: what if group_name or group_scope aren't provided?? + group = self.post(path="me/access-groups/{}".format(access_group_id), payload={"name": access_group_name, "scope": access_group_scope}) + return group + + """ Delete access group + """ + def delete_access_group(self, access_group_id): + # TODO: what if group_name or group_scope aren't provided?? + return self.delete(path="me/access-groups/{}".format(access_group_id)) + + """ Get access group resources by id + """ + def get_access_group_resources(self, access_group_id): + return self.get("me/access-groups/{}/resources".format(access_group_id)) + + """ Get resource from access group + """ + def get_access_group_resource(self, access_group_id, resource_id): + return self.get("ame/ccess-groups/{}/resources/{}".format(access_group_id, resource_id)) + + """ Delete resource from access group + """ + def delete_access_group_resource(self, access_group_id, resource_id): + return self.delete("me/access-groups/{}/resources/{}".format(access_group_id, resource_id)) + + """ Get access group users + """ + def get_access_group_users(self, access_group_id): + return self.get("me/access-groups/{}/users".format(access_group_id)) + + """ Add user to access group + """ + def add_access_group_user(self, access_group_id, email): + return self.post("me/access-groups/{}/users".format(access_group_id), payload={"email": email}) + + """ Get user from access group + """ + def get_access_group_user(self, access_group_id, user_id): + return self.get("me/access-groups/{}/users/{}".format(access_group_id, user_id)) + + """ Delete user from access group + """ + def delete_access_group_user(self, access_group_id, user_id): + return self.delete("me/access-groups/{}/users/{}".format(access_group_id, user_id)) + + """ Share device group with access group + """ + def share_device_group(self, device_group_id, access_group_id): + return self.post("me/device-groups/{}/share".format(device_group_id), payload={"accessGroupId": access_group_id}) + + """ Share file set with access group + """ + def share_file_set(self, file_set_id, access_group_id): + return self.post("me/file-sets/{}/share".format(file_set_id), payload={"accessGroupId": access_group_id}) + + """ Share file with access group + """ + def share_file(self, file_id, access_group_id): + return self.post("me/files/{}/share".format(file_id), payload={"accessGroupId": access_group_id}) + + """ Share project with access group + """ + def share_project(self, project_id, access_group_id): + return self.post("me/projects/{}/share".format(project_id), payload={"accessGroupId": access_group_id}) + + def get_parser(self): class MyParser(OptionParser): def format_epilog(self, formatter): @@ -905,11 +990,11 @@ def format_epilog(self, formatter): Download test run screenshots. Screenshots will be downloaded to current directory in a structure: [test-run-id]/[device-run-id]-[device-name]/screenshots/... - jobs Get list of your jobs - builds Get list of your builds + jobs Get list of your jobs + builds Get list of your builds create-job Create a new job. Job configuration in Jenkins pipeline format See the sample of Jenkisfile in http://docs.bitbar.com/build-service/guide.html - update-job + update-job Update existing job create-build Create a new build job. See https://cloud.testdroid.com/cloud/swagger-ui.html for details of build configuration @@ -918,6 +1003,32 @@ def format_epilog(self, formatter): download-builds-files Download all the results of the specific build wait-build Await completion (polling) of the build + access-groups Get access groups + access-group Get an access group by id + access-group-create Create a new access group + access-group-update + Update an access group + access-group-delete Delete an access group + access-group-resources Get resources in an access group + access-group-resource + Get a resource in an access group by id + access-group-resource-remove + Remove a resource from an access group + access-group-users Get users in an access group + access-group-users-get + Get a user in an access group + access-group-users-add + Add a user to an access group + access-group-users-remove + Remove a user from an access group + + share-device-group + Share a device group with an access group + share-file-set + Share a file set with an access group + share-file Share a file with an access group + share-project + Share a project with an access group """ parser = MyParser(usage=usage, description=description, epilog=epilog, version="%s %s" % ("%prog", __version__)) parser.add_option("-k", "--apikey", dest="apikey", @@ -937,7 +1048,7 @@ def format_epilog(self, formatter): return parser def get_commands(self): - commands = { + return { "me": self.get_me, "device-groups": self.print_device_groups, "available-free-devices": self.print_available_free_devices, @@ -968,10 +1079,24 @@ def get_commands(self): "delete-job": self.delete_job, "delete-build": self.delete_build, "download-builds-files": self.download_build_output_files, - "wait-build": self.wait_build - + "wait-build": self.wait_build, + "access-groups": self.get_access_groups, + "access-group": self.get_access_group, + "access-group-create": self.create_access_group, + "access-group-update": self.update_access_group, + "access-group-delete": self.delete_access_group, + "access-group-resources": self.get_access_group_resources, + "access-group-resource": self.get_access_group_resource, + "access-group-resource-remove": self.delete_access_group_resource, + "access-group-users": self.get_access_group_users, + "access-group-users-add": self.add_access_group_user, + "access-group-users-get": self.get_access_group_user, + "access-group-users-remove": self.delete_access_group_user, + "share-device-group": self.share_device_group, + "share-file-set": self.share_file_set, + "share-file": self.share_file, + "share-project": self.share_project, } - return commands def cli(self, parser, commands): (options, args) = parser.parse_args() @@ -985,7 +1110,7 @@ def cli(self, parser, commands): if sys.version_info[0] > 2: http.client.HTTPConnection.debuglevel = 1 else: - httplib.HTTPConnection.debuglevel = 1 + httplib.HTTPConnection.debuglevel = 1 logging.getLogger().setLevel(logging.DEBUG) requests_log = logging.getLogger("requests.packages.urllib3") requests_log.setLevel(logging.DEBUG) From cce0e45bdee8d3d69c03dfcda2391932be8b4825 Mon Sep 17 00:00:00 2001 From: Atte Keltanen Date: Tue, 8 Jan 2019 14:44:01 -0800 Subject: [PATCH 3/3] add badge for pypi --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2f0b1b1..74683c7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![PyPI version](https://badge.fury.io/py/testdroid.svg)](https://badge.fury.io/py/testdroid) [![Build Status](https://travis-ci.org/bitbar/testdroid-api-client-python.svg?branch=devel)](https://travis-ci.org/bitbar/testdroid-api-client-python) Python client for Testdroid Cloud APIv2