Package: qsaas
StatusqsaasA wrapper for the Qlik Sense Enterprise SaaS APIs.Intended audienceDevelopers -- familiarity with theQlik Sense Enterprise APIsis required.High-level benefits:Automatic pagination on GETs, returning all results.Ability to asynchronously make POSTs, PUTs, and PATCHs, where one can input the amount of threads you'd like (default is 10), dramatically decreasing processing time.Ease of establishing and managing connections to multiple tenants.Ability to connect to any API endpoint.Table of ContentsInstallationConfigurationBasic UsageAdvanced UsageComplete list of functionsAdditional NotesInstallationpip install qsaasDependencies (auto-installed by pip):requests
requests_toolbelt
aiohttp
asyncioConfigurationTo import qsaas, as qsaas only currently has one class,Tenant, the simplest way is:fromqsaas.qsaasimportTenantFrom there, one can instantiate a newTenantobject by executingq=Tenant(<args>)Option AIf connecting locally, the simplest way can be to use a json file for each tenant, as these files can be securely stored locally. The file must be a valid json file with the following structure:{"api_key":"<API_KEY>","tenant_fqdn":"<TENANT>.<REGION>.qlikcloud.com","tenant_id":"<TENANT_ID>"}When creating a newTenantobject then, the named paramconfigcan be used as follows (in this case, the file name is "config.json":q=Tenant(config="config.json")Option BIf it's preferred to feed in the api_key, tenant_fqdn, and tenant_id in a different manner, one can instead execute:q=Tenant(api_key=<API_KEY>,tenant=<TENANT_FQDN>,tenant_id=<TENANT_ID>)Basic UsageGet all users from a tenant and print their IDsusers=q.get('users')foruserinusers:print(user['id'])Get a specific user from a tenantuser=q.get('users',params={"subject":"QLIK-POC\dpi"})Get all apps from a tenant and print their namesapps=q.get('items',params={"resourceType":"app"})forappinapps:print(app['name'])Get all spaces from a tenantspaces=q.get('spaces')Create a new userbody={"tenantId":q.tenant_id,"subject":'WORKSHOP\\Qlik1',"name":'Qlik1',"email":'Qlik1@workshop.com',"status":"active"}q.post('users',body)Reload an applicationreload=q.post('reloads',json.dumps({"appId":"<APP_ID>"}))Reload an application and waitreload_id=q.post('reloads',json.dumps({"appId":"<APP_ID>"}))['id']status=Nonewhilestatusnotin['SUCCEEDED','FAILED']:time.sleep(1)status=q.get('reloads/'+reload_id)['status']Publish an applicationapp_id=<APP_ID>space_id=<SPACE_ID>app=q.post('apps/'+app_id+'/publish',json.dumps({"spaceId":space_id}))payload={"name":app['attributes']['name'],"resourceId":app['attributes']['id'],"description":app['attributes']['description'],"resourceType":"app","resourceAttributes":app['attributes'],"resourceCustomAttributes":{},"resourceCreatedAt":app['attributes']['createdDate'],"resourceCreatedBySubject":app['attributes']['owner'],"spaceId":space_id}q.post('items',json.dumps(payload))Change the owner of an applicationapp_id=<APP_ID>user_id=<USER_ID>q.put('apps/'+app_id+'/owner',json.dumps({"ownerId":user_id}))Import an applicationwithopen('<APP-NAME>.qvf','rb')asf:data=f.read()app=q.post('apps/import',data,params={"name":"<APP-NAME-NEW>"})payload={"name":app['attributes']['name'],"resourceId":app['attributes']['id'],"description":app['attributes']['description'],"resourceType":"app","resourceAttributes":app['attributes'],"resourceCustomAttributes":{},"resourceCreatedAt":app['attributes']['createdDate'],"resourceCreatedBySubject":app['attributes']['owner']}q.post('items',json.dumps(payload))Advanced UsageUpload a file to DataFilesfile_path=directory_path+'\\'+file_namebody=open(file_path,'rb')q.post('qix-datafiles',body,params={"connectionId":conn_id,"name":file_name})Asynchronously reload multiple applicationsNote:The default threading is 10 at a time--to modify this, add the named paramchunks=x, where x is an integer. Do not make this integer too high to avoid rate limiting.app_ids=['<APP_ID1>','<APP_ID2>','<APP_ID3>']payloads=[json.dumps({"appId":app_id})forapp_idinapp_ids]q.async_post('reloads',payloads=payloads)Asynchronously delete apps that have the name "delete_me"Note:This process currently requires deleting both from theappsanditemsendpoints. The default threading is 10 at a time--to modify this, add the named paramchunks=x, where x is an integer. Do not make this integer too high to avoid rate limiting.items=q.get('items',params={"resourceType":"app","name":"delete_me"})delete_dict={}delete_dict['items']=[item['id']foriteminitems]delete_dict['apps']=[item['resourceId']foriteminitems]foreindelete_dict:q.async_delete(e,ids=delete_dict[e])Asychronously add usersNote:The default threading is 10 at a time--to modify this, add the named paramchunks=x, where x is an integer. Do not make this integer too high to avoid rate limiting.payloads=[]foriinrange(10):user_subject='WORKSHOP\\Qlik'+str(i+1)user_name='Qlik'+str(i+1)user_email='Qlik'+str(i+1)+'@workshop.com'body={"tenantId":q.tenant_id,"subject":user_subject,"name":user_name,"email":user_email,"status":"active"}payloads.append(body)q.async_post('users',payloads=payloads)Asynchronously copy applications and assign them to new ownersNote:This is the only "custom" style function in all of qsaas, due to the fact that it has hardcoded endpoints and has an multi-step process--as it can copy applications and then assign those applications ot new owners in one go. The default threading is 10 at a time--to modify this, add the named paramchunks=x, where x is an integer. Do not make this integer too high to avoid rate limiting.Copy app and assign ownership to new usersq.async_app_copy('<GUID>',users=['<UserId-1>','<UserId-2>'])Simply copy an app 10 times, without assigning new ownershipq.async_app_copy('<GUID>',copies=10)Customize HeadersNote:This is available for all functions, but should largely not be needed (most headers are automatically generated by the Python libraries used). If the need arises to pass in custom headers, or to overwrite the existing headers, one can leverage the keyword paramheadersas per below.Upload an image to an applicationdata=open('your_image.png','rb').read()q.put('apps/<GUID>/media/files/your_image.png',data,headers={"Content-Type":"image/png"})Complete list of functionsq.get()q.post()q.put()q.patch()q.delete()q.async_post()q.async_put()q.async_patch()q.async_app_copy()*only custom functionFor each function, one can always refer to the docstring for a helpful description, and most provide examples. For instance,help(q.get)will output:Description
    --------------------
    GETs and paginates all results. Takes optional params.

    Mandatory parameters
    --------------------
    endpoint (str), exclude api/{version}

    Optional parameters
    --------------------
    params (dict)

    Example Usage
    --------------------
    Example 1:
        get('users')

        This will return all users.

    Example 2:
        get('items', params={"resourceType":"app"})

        This will return all apps from items.Additional NotesAPI DocumentationIt is highly encouraged to review the API documentation atthe qlik.dev portal. As this wrapper does not have wrapped Qlik functions (aside from theasync_app_copyfunction), it is integral to know the API appropriate endpoints to call.SupportThis project is built and maintained by Daniel Pilla, a Principal Analytics Platform Architect at Qlik. This project is however not supported by Qlik, and is only supported through Daniel.