Package: domolibrary
domolibrary: a powerful pydomo alternativeFormattinguse_snake_case for everything except class names is_bool - prefix any
boolean parameters with is_boolPatterns to recycleUPSERTdomolibrary.classes.DomoUser upsert_usersearch entityuser_routes.SearchUser_NoResults()What is it?domolibraryis 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’sDomo
Documentationpage.This library was created byDataCrewcontributor Jae Wilson.InstallThe DataCrew team is hard at work expanding the list of availableclassesandroutes. We have a ton of work completed, it’s just a
matter of migrating and documenting the code into this library.pipinstalldomolibraryHow to useAuthenticationFor each task, consider the appropriateDomoAuthmechanism. In most casesDomoFullAuthorDomoTokenAuthwill be appropriate as this library predominately accesses private APIs.Any Public routes or methods will be labeled appropriately in which case
you should useDomoDeveloperAuth.
Public routes are APIs enumerated and documented underDeveloper.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 methodimportosimportdomolibrary.client.DomoAuthasdmdatoken_auth=dmda.DomoTokenAuth(domo_instance="domo-community",domo_access_token=os.environ["DOMO_DOJO_ACCESS_TOKEN"],)Option 1: class based programmingIn this project domo entities,DomoActivityLog,DomoDatasetare all prefixed ‘Domo’ and can be found in theclassesfolder. Each
class method will call one or moreroutes. 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_from_idis a classmethod.Note: DomoLibrary uses the asynchronousaiohttprequests 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_from_id(auth=token_auth, dataset_id=os.environ['DOJO_DATASET_ID'])# domo_dsOnce 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 theDomoDataset_Schemawhich consists of subclassDomoDataset_Schema_Columnusing theDomoDataset_Schema.getmethod.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__Option 2 functional programmingAlthough classes add a pretty wrapper for interacting with Domo APIs,
users can opt to interact directly with APIs by way ofroutes.All route functions will exclusively call one API and will always return
aResponseGetDataobject OR raise anExceptionif appropriate.For example we can implement similar functionality as the Option 1
example by calling theget_dataset_by_idfunction.importdomolibrary.routes.datasetasdataset_routesds_res=awaitdataset_routes.get_dataset_by_id(auth=token_auth,dataset_id=os.environ["DOJO_DATASET_ID"])ds_resadjusting num_stacks_to_drop, consider revising `get_traceback` call
{16, 3, 12}

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': 1694281720000, '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}, '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}]}}}}, 'state': 'SUCCESS', 'validConfiguration': True, 'validAccount': True, 'streamId': 825, 'transportType': 'API', 'adc': False, 'adcExternal': False, 'cloudId': 'domo', 'cloudName': 'Domo', 'permissions': 'READ_WRITE_DELETE_SHARE_ADMIN', 'hidden': False, 'tags': '["Sep-09-2023 17:48","developer_documentation","hackercore"]', 'scheduleActive': True, 'cardCount': 2, 'cryoStatus': 'ADRENALINE'}, is_success=True, parent_class=None, traceback_details=TracebackDetails(function_name='get_dataset_by_id', file_name='/workspaces/domo_library/domolibrary/routes/dataset.py', function_trail='<module> -> get_dataset_by_id', traceback_stack=[<FrameSummary file /tmp/ipykernel_16238/1373906764.py, line 3 in <module>>, <FrameSummary file /workspaces/domo_library/domolibrary/routes/dataset.py, line 171 in get_dataset_by_id>], parent_class=None))ResponseGetDatawill always include a booleanis_success, the APIstatus, and raw
APIresponse.Typically the route methods will not alter the response unless the API
does not include a descriptive response (ex,routes.dataset.set_dataset_tagsdoes not return a response so we
artificially alter the response in the function.)[(prop,type(getattr(ds_res,prop)))forpropindir(ds_res)ifnotprop.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': 1694281720000,
 '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},
    '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}]}}}},
 'state': 'SUCCESS',
 'validConfiguration': True,
 'validAccount': True,
 'streamId': 825,
 'transportType': 'API',
 'adc': False,
 'adcExternal': False,
 'cloudId': 'domo',
 'cloudName': 'Domo',
 'permissions': 'READ_WRITE_DELETE_SHARE_ADMIN',
 'hidden': False,
 'tags': '["Sep-09-2023 17:48","developer_documentation","hackercore"]',
 'scheduleActive': True,
 'cardCount': 2,
 'cryoStatus': 'ADRENALINE'}Access Paginated APIs using the LooperA hidden advantage of using the DomoLibrary is that paginated API
requests are baked into the route’s definition.Considerquery_dataset_privatefrom theroutes.dataset.Inside this function we are usinglooperfromclient.get_datato paginate over the API response.known errorsutils/upload_data is using a general try/exceptbootstrap route raising a general exceptionintegrations/role_hierarchy raising general exceptionintegrations/domoJupyter raising general exceptionDomoUser general Exception