@@ -32,10 +32,10 @@ def __init__(self, msg):
32
32
class RequestResponseError (Exception ):
33
33
34
34
def __init__ (self , msg , status_code ):
35
- super (Exception , self ).__init__ ("Request Error: code %s: %s" %
35
+ super (Exception , self ).__init__ ("Request Error: code %s: %s" %
36
36
(status_code , msg ) )
37
37
self .status_code = status_code
38
-
38
+
39
39
40
40
""" Format unix timestamp to human readable. Automatically detects timestamps with seconds or milliseconds.
41
41
"""
@@ -324,7 +324,7 @@ def print_available_free_android_devices(self, limit=0):
324
324
if device ['creditsPrice' ] == 0 and device ['locked' ] == False and device ['osType' ] == "ANDROID" :
325
325
print (device ['displayName' ])
326
326
print ("" )
327
-
327
+
328
328
""" Print available frameworks
329
329
"""
330
330
def print_available_frameworks (self , os_type = None , limit = 0 ):
@@ -387,7 +387,7 @@ def get_project(self, project_id):
387
387
def print_projects (self , limit = 0 ):
388
388
me = self .get_me ()
389
389
print ("Projects for %s <%s>:" % (me ['name' ], me ['email' ]))
390
-
390
+
391
391
for project in self .get_projects (limit )['data' ]:
392
392
print ("%s %s \" %s\" " % (str (project ['id' ]).ljust (10 ), project ['type' ].ljust (15 ), project ['name' ]))
393
393
@@ -468,9 +468,9 @@ def set_project_framework(self, project_id, frameworkId):
468
468
469
469
470
470
""" Start a test run using test run config
471
- e.g '{"frameworkId":12252,
472
- "osType": "ANDROID",
473
- "projectId":1234,
471
+ e.g '{"frameworkId":12252,
472
+ "osType": "ANDROID",
473
+ "projectId":1234,
474
474
"files":[{"id":9876}, {"id":5432}]
475
475
"testRunParameters":[{"key":"xyz", "value":"abc"}],
476
476
"deviceGroupId":6854
@@ -629,7 +629,7 @@ def get_device_run_files(self, project_id, test_run_id, device_session_id, tags=
629
629
else :
630
630
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 ))
631
631
632
- """ Get list of input files
632
+ """ Get list of input files
633
633
"""
634
634
def get_input_files (self , limit = 0 ):
635
635
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):
650
650
for build in self .get_builds (job_id , limit )['data' ]:
651
651
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' ]))
652
652
653
-
654
-
653
+
654
+
655
655
""" Get builds from the job
656
656
"""
657
657
def get_builds (self , job_id , limit = 0 ):
@@ -692,14 +692,14 @@ def create_job(self, job_name, content, job_type="BUILD"):
692
692
isDirectory
693
693
fileUrlEnvVariable
694
694
695
- usage: client.create_build(job_id, json.dumps({"fileId":123213...))
695
+ usage: client.create_build(job_id, json.dumps({"fileId":123213...))
696
696
"""
697
697
def create_build (self , job_id , build_config = {}):
698
698
build = self .post (path = "me/jobs/{}/builds" .format (job_id ), payload = build_config , headers = {'Content-type' : 'application/json' , 'Accept' : 'application/json' })
699
699
logger .info ("build %s: %s (%s) " % (build ['id' ], build ['buildNumber' ], build ['state' ] ))
700
700
return build
701
701
702
- """ Update job
702
+ """ Upload job
703
703
"""
704
704
def upload_job (self , job_id ,job_name , content ):
705
705
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):
756
756
while True :
757
757
time .sleep (self .polling_interval_mins * 6 )
758
758
if not self .api_key :
759
- self .access_token = None
759
+ self .access_token = None
760
760
self .get_token ()
761
761
buildStatus = self .get_build (job_id , build_id )
762
762
if buildStatus and 'state' in buildStatus :
@@ -857,6 +857,91 @@ def download_test_screenshots(self, project_id, test_run_id):
857
857
else :
858
858
logger .info ("Device %s has errored or has not finished - skipping" % device_run ['device' ]['displayName' ])
859
859
860
+ """ Get access groups
861
+ """
862
+ def get_access_groups (self ):
863
+ return self .get ("me/access-groups" )
864
+
865
+ """ Get access group by id
866
+ """
867
+ def get_access_group (self , access_group_id ):
868
+ return self .get ("me/access-groups/{}" .format (access_group_id ))
869
+
870
+ """ Create access group
871
+ """
872
+ def create_access_group (self , access_group_name , access_group_scope = "USER" ):
873
+ group = self .post (path = "me/access-groups" , payload = {"name" : access_group_name , "scope" : access_group_scope })
874
+ return group
875
+
876
+ """ Update access group
877
+ """
878
+ def update_access_group (self , access_group_id , access_group_name , access_group_scope ):
879
+ # TODO: what if group_name or group_scope aren't provided??
880
+ group = self .post (path = "me/access-groups/{}" .format (access_group_id ), payload = {"name" : access_group_name , "scope" : access_group_scope })
881
+ return group
882
+
883
+ """ Delete access group
884
+ """
885
+ def delete_access_group (self , access_group_id ):
886
+ # TODO: what if group_name or group_scope aren't provided??
887
+ return self .delete (path = "me/access-groups/{}" .format (access_group_id ))
888
+
889
+ """ Get access group resources by id
890
+ """
891
+ def get_access_group_resources (self , access_group_id ):
892
+ return self .get ("me/access-groups/{}/resources" .format (access_group_id ))
893
+
894
+ """ Get resource from access group
895
+ """
896
+ def get_access_group_resource (self , access_group_id , resource_id ):
897
+ return self .get ("ame/ccess-groups/{}/resources/{}" .format (access_group_id , resource_id ))
898
+
899
+ """ Delete resource from access group
900
+ """
901
+ def delete_access_group_resource (self , access_group_id , resource_id ):
902
+ return self .delete ("me/access-groups/{}/resources/{}" .format (access_group_id , resource_id ))
903
+
904
+ """ Get access group users
905
+ """
906
+ def get_access_group_users (self , access_group_id ):
907
+ return self .get ("me/access-groups/{}/users" .format (access_group_id ))
908
+
909
+ """ Add user to access group
910
+ """
911
+ def add_access_group_user (self , access_group_id , email ):
912
+ return self .post ("me/access-groups/{}/users" .format (access_group_id ), payload = {"email" : email })
913
+
914
+ """ Get user from access group
915
+ """
916
+ def get_access_group_user (self , access_group_id , user_id ):
917
+ return self .get ("me/access-groups/{}/users/{}" .format (access_group_id , user_id ))
918
+
919
+ """ Delete user from access group
920
+ """
921
+ def delete_access_group_user (self , access_group_id , user_id ):
922
+ return self .delete ("me/access-groups/{}/users/{}" .format (access_group_id , user_id ))
923
+
924
+ """ Share device group with access group
925
+ """
926
+ def share_device_group (self , device_group_id , access_group_id ):
927
+ return self .post ("me/device-groups/{}/share" .format (device_group_id ), payload = {"accessGroupId" : access_group_id })
928
+
929
+ """ Share file set with access group
930
+ """
931
+ def share_file_set (self , file_set_id , access_group_id ):
932
+ return self .post ("me/file-sets/{}/share" .format (file_set_id ), payload = {"accessGroupId" : access_group_id })
933
+
934
+ """ Share file with access group
935
+ """
936
+ def share_file (self , file_id , access_group_id ):
937
+ return self .post ("me/files/{}/share" .format (file_id ), payload = {"accessGroupId" : access_group_id })
938
+
939
+ """ Share project with access group
940
+ """
941
+ def share_project (self , project_id , access_group_id ):
942
+ return self .post ("me/projects/{}/share" .format (project_id ), payload = {"accessGroupId" : access_group_id })
943
+
944
+
860
945
def get_parser (self ):
861
946
class MyParser (OptionParser ):
862
947
def format_epilog (self , formatter ):
@@ -905,11 +990,11 @@ def format_epilog(self, formatter):
905
990
Download test run screenshots. Screenshots will be downloaded to
906
991
current directory in a structure:
907
992
[test-run-id]/[device-run-id]-[device-name]/screenshots/...
908
- jobs Get list of your jobs
909
- builds <job-id> Get list of your builds
993
+ jobs Get list of your jobs
994
+ builds <job-id> Get list of your builds
910
995
create-job <job-name> <job-configuration> Create a new job. Job configuration in Jenkins pipeline format
911
996
See the sample of Jenkisfile in http://docs.bitbar.com/build-service/guide.html
912
- update-job <job-id> <job-name> <job-configuration>
997
+ update-job <job-id> <job-name> <job-configuration>
913
998
Update existing job
914
999
create-build <job-id> <build-configuration> Create a new build job. See https://cloud.testdroid.com/cloud/swagger-ui.html
915
1000
for details of build configuration
@@ -918,6 +1003,32 @@ def format_epilog(self, formatter):
918
1003
download-builds-files <job-id> <build-id> Download all the results of the specific build
919
1004
wait-build <job-id> <build-id> Await completion (polling) of the build
920
1005
1006
+ access-groups Get access groups
1007
+ access-group <access-group-id> Get an access group by id
1008
+ access-group-create <name> <scope> Create a new access group
1009
+ access-group-update <access-group-id> <name> <scope>
1010
+ Update an access group
1011
+ access-group-delete <access-group-id> Delete an access group
1012
+ access-group-resources <access-group-id> Get resources in an access group
1013
+ access-group-resource <access-group-id> <resource-id>
1014
+ Get a resource in an access group by id
1015
+ access-group-resource-remove <access-group-id> <resource-id>
1016
+ Remove a resource from an access group
1017
+ access-group-users <access-group-id> Get users in an access group
1018
+ access-group-users-get <access-group-id> <user-id>
1019
+ Get a user in an access group
1020
+ access-group-users-add <access-group-id> <user-email>
1021
+ Add a user to an access group
1022
+ access-group-users-remove <access-group-id> <user-email>
1023
+ Remove a user from an access group
1024
+
1025
+ share-device-group <device-group-id> <access-group-id>
1026
+ Share a device group with an access group
1027
+ share-file-set <file-set-id> <access-group-id>
1028
+ Share a file set with an access group
1029
+ share-file <file-id> <access-group-id> Share a file with an access group
1030
+ share-project <project-id> <access-group-id>
1031
+ Share a project with an access group
921
1032
"""
922
1033
parser = MyParser (usage = usage , description = description , epilog = epilog , version = "%s %s" % ("%prog" , __version__ ))
923
1034
parser .add_option ("-k" , "--apikey" , dest = "apikey" ,
@@ -937,7 +1048,7 @@ def format_epilog(self, formatter):
937
1048
return parser
938
1049
939
1050
def get_commands (self ):
940
- commands = {
1051
+ return {
941
1052
"me" : self .get_me ,
942
1053
"device-groups" : self .print_device_groups ,
943
1054
"available-free-devices" : self .print_available_free_devices ,
@@ -968,10 +1079,24 @@ def get_commands(self):
968
1079
"delete-job" : self .delete_job ,
969
1080
"delete-build" : self .delete_build ,
970
1081
"download-builds-files" : self .download_build_output_files ,
971
- "wait-build" : self .wait_build
972
-
1082
+ "wait-build" : self .wait_build ,
1083
+ "access-groups" : self .get_access_groups ,
1084
+ "access-group" : self .get_access_group ,
1085
+ "access-group-create" : self .create_access_group ,
1086
+ "access-group-update" : self .update_access_group ,
1087
+ "access-group-delete" : self .delete_access_group ,
1088
+ "access-group-resources" : self .get_access_group_resources ,
1089
+ "access-group-resource" : self .get_access_group_resource ,
1090
+ "access-group-resource-remove" : self .delete_access_group_resource ,
1091
+ "access-group-users" : self .get_access_group_users ,
1092
+ "access-group-users-add" : self .add_access_group_user ,
1093
+ "access-group-users-get" : self .get_access_group_user ,
1094
+ "access-group-users-remove" : self .delete_access_group_user ,
1095
+ "share-device-group" : self .share_device_group ,
1096
+ "share-file-set" : self .share_file_set ,
1097
+ "share-file" : self .share_file ,
1098
+ "share-project" : self .share_project ,
973
1099
}
974
- return commands
975
1100
976
1101
def cli (self , parser , commands ):
977
1102
(options , args ) = parser .parse_args ()
@@ -985,7 +1110,7 @@ def cli(self, parser, commands):
985
1110
if sys .version_info [0 ] > 2 :
986
1111
http .client .HTTPConnection .debuglevel = 1
987
1112
else :
988
- httplib .HTTPConnection .debuglevel = 1
1113
+ httplib .HTTPConnection .debuglevel = 1
989
1114
logging .getLogger ().setLevel (logging .DEBUG )
990
1115
requests_log = logging .getLogger ("requests.packages.urllib3" )
991
1116
requests_log .setLevel (logging .DEBUG )
0 commit comments