Connecting and using a client
To connect to a running Sugar server, use one of the provided client classes. These clients act as a middleware between some HTTP library and the other Zucker components. That means that every client type uses a different underlying transport library. You can use this to create your own client if you are already using another requests library that isn’t natively supported by Zucker yet. Further, the client implementation will determine whether actions will be synchronous or asychronous. In general, all clients need to be initialized with the URL of the server and a pair of OAuth credentials:
>>> crm = SomeClient("https://sugar.local", "username", "password")
Note
By default, this will connect using the zucker
API platform. Make sure
to create it in the admin section on the server or provide another platform
name using the client_platform
parameter. See the constructor
__init__()
documentation for more
parameters.
See the bottom of this page for a full list of clients. Once the client has been created, you can go ahead and define a data model that matches the server’s. This will be the main way to use Zucker’s ORM system.
Client API
Clients are declared as follows. This is the definition of the base superclass for all clients, synchronous and asynchronous:
- class zucker.client.base.BaseClient(base_url: str, username: str, password: str, *, client_platform: str = 'zucker', verify_ssl: bool = True)
Connection handler that handles communicating with a SugarCRM instance.
This is the main entry point for interfacing with models. After creating a new instance, the server’s modules are available as attributes on the client. Concrete client implementations (for different transport mechanisms) should implement one of
SyncClient
orAsyncClient
.- __init__(base_url: str, username: str, password: str, *, client_platform: str = 'zucker', verify_ssl: bool = True)
Construct a client instance.
This may take a few seconds, as it performs initial authentication as well as querying the server for metadata about it’s configuration.
- Parameters:
base_url – The URL of the SugarCRM installation to connect to.
username – Username to authenticate with.
password – Password to authenticate with.
client_platform – OAuth platform string.
verify_ssl – Set this to false to disable verification of the server’s SSL certificate. This should only be used while testing!
Closing
- abstract BaseClient.close() Union[None, Awaitable[None]]
Perform cleanup operations.
This method should be called when the client is no longer used. Further operations on the client or any of its modules are consider undefined behavior afterwards. Some clients may be able to reinitialize transparently after closing while others may be unusable.
Note
This method may be synchronous or asychronous, depending on the client.
Metadata
The Metadata API yields information about the Sugar server’s configuration and data model. If you would like to query this API, you might also want to read this blog post from Sugar. Since metadata fetching can take quite a while, it needs to be initiated explicitly:
- abstract BaseClient.fetch_metadata(*types: str) Union[None, Awaitable[None]]
Make sure server metadata for the given set of types is available.
Example. Pass a list of metadata types that should be cached to this method:
>>> crm.fetch_metadata("server_info", "full_module_list") >>> await async_crm.fetch_metadata("server_info")
Hint
Here is a non-exhaustive list of these type names:
hidden_subpanels
,currencies
,module_tab_map
,labels
,ordered_labels
,config
,relationships
,server_info
,logo_url
,languages
,full_module_list
,modules
,modules_info
,fields
,filters
,views
,layouts
,datas
Like close()
, this method may block or be awaitable, depending on the
client. The other following methods and properties (concerning server
metadata) only parse the information fetched here and are therefore
synchronous. In general, cached metadata entries can be retrieved like this:
- BaseClient.get_metadata_item(type_name: str) JsonMapping
Return the cached value of a given metadata item.
- Parameters:
type_name – Name of the metadata’s type, as defined by Sugar.
- Raises:
UnfetchedMetadataError – This error will be raised when the corresponding metadata has not been fetched yet. In that case, call
fetch_metadata()
to populate the cache.
Parts of the metadata API are also exposed via their own properties:
- property BaseClient.module_names: Iterator[str]
List of all modules that are available.
This requires fetching the
full_module_list
metadata item.
- property BaseClient.server_info: Tuple[str, str, str]
Sever information tuple.
This requires fetching the
server_info
metadata item.- Returns:
A 3-tuple that follows the syntax (
flavor
,version
,build
).
Supported modules
Clients expose the list of all supported modules under the
module_names
property:
>>> crm.fetch_metadata("full_module_list") # Otherwise an UnfetchedMetadataError is raised
>>> list(crm.module_names)
["Leads", "Contacts", ...]
To check if a given module is supported, use the in
operator:
>>> "Contacts" in crm
True
>>> "NonexistentModule" in crm
False
This call supports both modules names as strings as well as actual
Module
classes.
Generic requests
You can also use the client instance to perform generic authenticated HTTP requests against the server:
- abstract BaseClient.request(method: str, endpoint: str, *, params: Optional[Mapping[str, str]] = None, data: Optional[JsonMapping] = None, json: Optional[JsonMapping] = None) Union[JsonMapping, Awaitable[JsonMapping]]
Handle a request to the CRM’s REST API.
This will make sure that authentication is set up and the correct URL is built. Parameters are akin to those defined by popular HTTP libraries (although not all are supported).
- Parameters:
method – HTTP verb to use.
endpoint – Desired endpoint name (the part after /rest/v??_?/).
params – Dictionary of query parameters to add to the URL.
data – Request form data.
json – Request body that will be JSON-encoded. This is mutually exclusive with
data
.
Note
Depending on the client implementation, this method will be synchronous or asynchronous.
The first request will cause the authentication flow to run. Use this property if you need to check for that case:
- property BaseClient.authenticated: bool
Shows whether initial authentication with the server has already been performed.
Bulking
Asynchronous client implementations support batching together arbitrary requests into a single HTTP call:
- async AsyncClient.bulk(action_1: Awaitable[_T1], /) Tuple[_T1]
- async AsyncClient.bulk(action_1: Awaitable[_T1], action_2: Awaitable[_T2], /) Tuple[_T1, _T2]
- async AsyncClient.bulk(action_1: Awaitable[_T1], action_2: Awaitable[_T2], action_3: Awaitable[_T3], /) Tuple[_T1, _T2, _T3]
- async AsyncClient.bulk(action_1: Awaitable[_T1], action_2: Awaitable[_T2], action_3: Awaitable[_T3], action_4: Awaitable[_T4], /) Tuple[_T1, _T2, _T3, _T4]
- async AsyncClient.bulk(action_1: Awaitable[_T1], action_2: Awaitable[_T2], action_3: Awaitable[_T3], action_4: Awaitable[_T4], action_5: Awaitable[_T5], /) Tuple[_T1, _T2, _T3, _T4, _T5]
- async AsyncClient.bulk(action_1: Awaitable[_T1], action_2: Awaitable[_T2], action_3: Awaitable[_T3], action_4: Awaitable[_T4], action_5: Awaitable[_T5], action_6: Awaitable[_T6], /) Tuple[_T1, _T2, _T3, _T4, _T5, _T6]
- async AsyncClient.bulk(*actions: Awaitable[Any]) Tuple[Any, ...]
Run a sequence of actions that require server communication together.
This will use Sugar’s Bulk API to batch all actions together and send them as a single HTTP request. It works similarly to
asyncio.gather()
in that this method will resolve once the result for all provided awaitables is available and return them as a tuple. You can also use this method to gather together multiple awaitables where only one of them uses the server. If an action doesn’t resolve after the first request (for example because it needs a second one), a second batch will be started.Do not use this is threaded environments – the implementation is not thread-safe.
Here is an example usage:
first_record, _, last_record = await crm.bulk( MyModel.find()[0], some_record.delete(), MyModel.find()[-1] )
Note
Due to shortcomings of the Python typing system, this method will only yield the correct types for up to 6 arguments. Bulking more actions will work, but the returned type will be
Any
. In that case, you will need to usetyping.cast()
to force the correct types.
Bundled clients
Zucker currently bundles two client implementations:
RequestsClient
(synchronous)
AioClient
(asynchronous)
Implementing your own client
If you are already using an HTTP library that isn’t supported by Zucker yet,
you can write your own client. This isn’t much work — you basically only need
to implement the raw_request()
method. Choose one of
SyncClient
or
AsyncClient
as the base class. As a reference, have
a look at the provided client implementations — they also inherit from the two
aforementioned base classes.
- class zucker.client.base.SyncClient(base_url: str, username: str, password: str, *, client_platform: str = 'zucker', verify_ssl: bool = True)
- class zucker.client.base.AsyncClient(base_url: str, username: str, password: str, *, client_platform: str = 'zucker', verify_ssl: bool = True)