
Research
SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains
An emerging npm supply chain attack that infects repos, steals CI secrets, and targets developer AI toolchains for further compromise.
domolibrary
Advanced tools
use_snake_case for everything except class names is_bool - prefix any boolean parameters with is_bool
domolibrary.classes.DomoUser upsert_user
user_routes.SearchUser_NoResults()
domolibrary is a Python package that provides a OOP (class-based) and a functional approach to interacting with Domo’s API framework.
All accessed APIs are documented under DataCrew’s Domo Documentation page.
This library was created by DataCrew contributor Jae Wilson.
The DataCrew team is hard at work expanding the list of available
classes and routes. We have a ton of work completed, it’s just a
matter of migrating and documenting the code into this library.
pip install domolibrary
For each task, consider the appropriate
DomoAuth
mechanism. In most cases
DomoFullAuth
or
DomoTokenAuth
will be appropriate as this library predominately accesses private APIs.
Any Public routes or methods will be labeled appropriately in which case
you should use
DomoDeveloperAuth.
Public routes are APIs enumerated and documented under
Developer.Domo.com.
Typically each project will begin with configuring an auth object. If you are accessing multiple Domo instances, you’ll probably need multiple auth objects.
# configure an auth method
import os
import domolibrary.client.DomoAuth as dmda
token_auth = dmda.DomoTokenAuth(
domo_instance=os.environ['DOMO_INSTANCE'],
domo_access_token=os.environ["DOMO_ACCESS_TOKEN"],
)
In this project domo entities,
DomoActivityLog,
DomoDataset
are all prefixed ‘Domo’ and can be found in the classes folder. Each
class method will call one or more routes. Each route will interact
with one and only one API.
Although most methods will be standard methods that will be called after creating an instance of the class, some methods will be classmethods which return an instance of the class.
In the example below,
DomoDataset.get_by_id
is a classmethod.
Note: DomoLibrary uses the asynchronous aiohttp requests library to
offer users the ability to write concurrently executing code.
# import domolibrary.classes.DomoDataset as dmds
# # this is a class method
# domo_ds = await dmds.DomoDataset.get_by_id(auth=token_auth, dataset_id=os.environ['DOJO_DATASET_ID'])
# domo_ds
Once instantiated, you can call methods to interact with that object. You typically won’t have to pass auth creds again because they are saved to the object.
In the example below we are retrieving the
DomoDataset_Schema
which consists of subclass
DomoDataset_Schema_Column
using the
DomoDataset_Schema.get
method.
We take the approach of where possible converting dictionaries from Domo APIs into classes because it provides greater predictability when users are creating integrations between platforms (ex. Domo to Trello).
# await domo_ds.schema.get()
Typically all information about an entity is saved in the object
# domo_ds.__dict__
Although classes add a pretty wrapper for interacting with Domo APIs,
users can opt to interact directly with APIs by way of routes.
All route functions will exclusively call one API and will always return
a
ResponseGetData
object OR raise an Exception if appropriate.
For example we can implement similar functionality as the Option 1
example by calling the
get_dataset_by_id
function.
import domolibrary.routes.dataset as dataset_routes
ds_res = await dataset_routes.get_dataset_by_id(
auth=token_auth, dataset_id=os.environ["DOJO_DATASET_ID"]
)
ds_res
warning this token has not been validated by who_am_i, run get_auth_token first
ResponseGetData(status=200, response={'id': '04c1574e-c8be-4721-9846-c6ffa491144b', 'displayType': 'domo-jupyterdata', 'dataProviderType': 'domo-jupyterdata', 'type': 'Jupyter', 'name': 'domo_kbs', 'owner': {'id': '1893952720', 'name': 'Jae Wilson1', 'type': 'USER', 'group': False}, 'status': 'SUCCESS', 'created': 1668379680000, 'lastTouched': 1711222468000, 'lastUpdated': 1668385822045, 'rowCount': 1185, 'columnCount': 7, 'cardInfo': {'cardCount': 2, 'cardViewCount': 0}, 'properties': {'formulas': {'formulas': {'calculation_ca9d4b1c-f73a-4f76-9f94-d3c4ca6871c5': {'templateId': 2664, 'id': 'calculation_ca9d4b1c-f73a-4f76-9f94-d3c4ca6871c5', 'name': 'rowcount', 'formula': 'sum(1)', 'status': 'VALID', 'dataType': 'LONG', 'persistedOnDataSource': True, 'isAggregatable': True, 'bignumber': False, 'variable': False}, 'calculation_38846559-d190-4ab1-809b-bcd361db5670': {'templateId': 2665, 'id': 'calculation_38846559-d190-4ab1-809b-bcd361db5670', 'name': 'max_views', 'formula': 'max(views)', 'status': 'VALID', 'dataType': 'LONG', 'persistedOnDataSource': True, 'isAggregatable': True, 'bignumber': False, 'columnPositions': [{'columnName': 'views', 'columnPosition': 4}], 'variable': False}}}}, 'state': 'SUCCESS', 'validConfiguration': True, 'validAccount': True, 'streamId': 825, 'transportType': 'API', 'adc': True, 'adcExternal': False, 'adcSource': 'DIRECT', 'cloudId': 'domo', 'cloudName': 'Domo', 'permissions': 'READ_WRITE_DELETE_SHARE_ADMIN', 'hidden': False, 'tags': '["Mar-23-2024 13:34","developer_documentation","hackercore"]', 'scheduleActive': True, 'cardCount': 2, 'cryoStatus': 'ADRENALINE', 'cloudEngine': 'domo'}, is_success=True, parent_class=None)
ResponseGetData
will always include a boolean is_success, the API status, and raw
API response.
Typically the route methods will not alter the response unless the API
does not include a descriptive response (ex,
routes.dataset.set_dataset_tags does not return a response so we
artificially alter the response in the function.)
[
(prop, type(getattr(ds_res, prop)))
for prop in dir(ds_res)
if not prop.startswith("_")
]
[('auth', domolibrary.client.DomoAuth.DomoTokenAuth),
('is_success', bool),
('parent_class', NoneType),
('response', dict),
('set_response', method),
('status', int),
('traceback_details', domolibrary.client.Logger.TracebackDetails)]
ds_res.response
{'id': '04c1574e-c8be-4721-9846-c6ffa491144b',
'displayType': 'domo-jupyterdata',
'dataProviderType': 'domo-jupyterdata',
'type': 'Jupyter',
'name': 'domo_kbs',
'owner': {'id': '1893952720',
'name': 'Jae Wilson1',
'type': 'USER',
'group': False},
'status': 'SUCCESS',
'created': 1668379680000,
'lastTouched': 1711222468000,
'lastUpdated': 1668385822045,
'rowCount': 1185,
'columnCount': 7,
'cardInfo': {'cardCount': 2, 'cardViewCount': 0},
'properties': {'formulas': {'formulas': {'calculation_ca9d4b1c-f73a-4f76-9f94-d3c4ca6871c5': {'templateId': 2664,
'id': 'calculation_ca9d4b1c-f73a-4f76-9f94-d3c4ca6871c5',
'name': 'rowcount',
'formula': 'sum(1)',
'status': 'VALID',
'dataType': 'LONG',
'persistedOnDataSource': True,
'isAggregatable': True,
'bignumber': False,
'variable': False},
'calculation_38846559-d190-4ab1-809b-bcd361db5670': {'templateId': 2665,
'id': 'calculation_38846559-d190-4ab1-809b-bcd361db5670',
'name': 'max_views',
'formula': 'max(views)',
'status': 'VALID',
'dataType': 'LONG',
'persistedOnDataSource': True,
'isAggregatable': True,
'bignumber': False,
'columnPositions': [{'columnName': 'views', 'columnPosition': 4}],
'variable': False}}}},
'state': 'SUCCESS',
'validConfiguration': True,
'validAccount': True,
'streamId': 825,
'transportType': 'API',
'adc': True,
'adcExternal': False,
'adcSource': 'DIRECT',
'cloudId': 'domo',
'cloudName': 'Domo',
'permissions': 'READ_WRITE_DELETE_SHARE_ADMIN',
'hidden': False,
'tags': '["Mar-23-2024 13:34","developer_documentation","hackercore"]',
'scheduleActive': True,
'cardCount': 2,
'cryoStatus': 'ADRENALINE',
'cloudEngine': 'domo'}
A hidden advantage of using the DomoLibrary is that paginated API requests are baked into the route’s definition.
Consider
query_dataset_private
from the routes.dataset.
Inside this function we are using
looper
from client.get_data to paginate over the API response.
FAQs
Unknown package
We found that domolibrary 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.

Research
An emerging npm supply chain attack that infects repos, steals CI secrets, and targets developer AI toolchains for further compromise.

Company News
Socket is proud to join the OpenJS Foundation as a Silver Member, deepening our commitment to the long-term health and security of the JavaScript ecosystem.

Security News
npm now links to Socket's security analysis on every package page. Here's what you'll find when you click through.