Security News
Fluent Assertions Faces Backlash After Abandoning Open Source Licensing
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
A wrapper for the Qlik Sense Enterprise SaaS APIs.
Developers -- familiarity with the Qlik Sense Enterprise APIs is required.
pip install qsaas
requests
requests_toolbelt
aiohttp
asyncio
To import qsaas, as qsaas only currently has one class, Tenant
, the simplest way is:
from qsaas.qsaas import Tenant
From there, one can instantiate a new Tenant
object by executing
q = Tenant(<args>)
If 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 new Tenant
object then, the named param config
can be used as follows (in this case, the file name is "config.json":
q = Tenant(config="config.json")
If 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>)
users = q.get('users')
for user in users:
print(user['id'])
user = q.get('users', params={"subject":"QLIK-POC\dpi"})
apps = q.get('items', params={"resourceType":"app"})
for app in apps:
print(app['name'])
spaces = q.get('spaces')
body = {
"tenantId": q.tenant_id,
"subject": 'WORKSHOP\\Qlik1',
"name": 'Qlik1',
"email": 'Qlik1@workshop.com',
"status": "active"
}
q.post('users', body)
reload = q.post('reloads', json.dumps({"appId": "<APP_ID>"}))
reload_id = q.post('reloads', json.dumps({"appId": "<APP_ID>"}))['id']
status = None
while status not in ['SUCCEEDED', 'FAILED']:
time.sleep(1)
status = q.get('reloads/' + reload_id)['status']
app_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))
app_id = <APP_ID>
user_id = <USER_ID>
q.put('apps/' + app_id + '/owner', json.dumps({"ownerId": user_id}))
with open('<APP-NAME>.qvf', 'rb') as f:
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))
file_path = directory_path + '\\' + file_name
body = open(file_path, 'rb')
q.post('qix-datafiles', body,
params={"connectionId": conn_id, "name": file_name})
Note: The default threading is 10 at a time--to modify this, add the named param chunks=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}) for app_id in app_ids]
q.async_post('reloads', payloads=payloads)
Note: This process currently requires deleting both from the apps
and items
endpoints. The default threading is 10 at a time--to modify this, add the named param chunks=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'] for item in items]
delete_dict['apps'] = [item['resourceId'] for item in items]
for e in delete_dict:
q.async_delete(e, ids=delete_dict[e])
Note: The default threading is 10 at a time--to modify this, add the named param chunks=x
, where x is an integer. Do not make this integer too high to avoid rate limiting.
payloads = []
for i in range(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)
Note: 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 param chunks=x
, where x is an integer. Do not make this integer too high to avoid rate limiting.
Copy app and assign ownership to new users
q.async_app_copy('<GUID>',users=['<UserId-1>','<UserId-2>'])
Simply copy an app 10 times, without assigning new ownership
q.async_app_copy('<GUID>',copies=10)
Note: 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 param headers
as per below.
Upload an image to an application
data = open('your_image.png', 'rb').read()
q.put('apps/<GUID>/media/files/your_image.png',
data, headers={"Content-Type": "image/png"})
q.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.
It is highly encouraged to review the API documentation at the qlik.dev portal. As this wrapper does not have wrapped Qlik functions (aside from the async_app_copy
function), it is integral to know the API appropriate endpoints to call.
This 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.
FAQs
A Python wrapper for the Qlik Sense Enterprise SaaS APIs.
We found that qsaas demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
Research
Security News
Socket researchers uncover the risks of a malicious Python package targeting Discord developers.
Security News
The UK is proposing a bold ban on ransomware payments by public entities to disrupt cybercrime, protect critical services, and lead global cybersecurity efforts.