2.2.5. Proximie REST services
The Proximie infrastructure provides a comprehensive set of REST APIs for accessing the Proximie services. These are described in detail on Proximie’s API Developer Hub.
The Proximie::PxRestApi namespace provides high level support for key Proximie REST APIs for use with SDK applications.
2.2.5.1. Access tokens
All requests need to provide an access token, which allows the Proximie services to know what the requestor is authorised to access. Access tokens are obtained via an authentication flow using requests with Proximie’s authentication service.
Note that access tokens have a limited life span; depending on the service or tenant being accessed, this can range from minutes to hours. Applications need to handle token expiry and refresh flows to keep an up-to-date and valid token.
For more information on obtaining and managing access tokens, see Authentication & authorisation.
2.2.5.2. Using PxRestApi::ProximieServices
An application can use a ProximieServices object
to make API calls to the Proximie service. The Proximie APIs are numerous and the
SDK ProximieServices implements a limited useful subset of functions and types.
This set will grow as the SDK continues to develop.
Creating a ProximieServices instance
To create a new ProximieServices object, the application needs to provide:
A
ProximieContextfor the applicationA
ServiceSettingsobject providing details on how to access the service - e.g. the host name and portA
RequestFieldsobject providing request fields to be used for each request
using ProximieServices = Proximie::PxRestApi::ProximieServices;
ProximieServices::ServiceSettings service;
service.host = "my.proximie.net";
ProximieServices::RequestFields reqFields;
reqFields.tokenProvider(auth);
ProximieServices pxapi(pxcontext, service, reqFields);
from px_core import rest
service = rest.ProximieServices.ServiceSettings()
service.host = "my.proximie.net"
req_fields = rest.ProximieServices.RequestFields(auth)
services = rest.ProximieServices(pxcontext, service, req_fields)
We assume pxcontext is already created (see ProximieContext for details).
The ServiceSettings object
simply has the host set (this will differ depending on which Proximie
environment or tenant you are using).
Then, the request fields are set. Here, we just set a tokenProvider,
which is an object that the application creates. It is responsible, upon request,
to provide a valid access token for a REST call. See Authentication & authorisation for more
information on token providers.
Making REST API requests
Once you have your ProximieServices object, you can use its member functions to make API requests. As mentioned previously only a subset of the full Proximie APIs are implemented, but the functions provided use native types for ease of calling and obtaining results.
In C++, the API functions typically come in two forms: with a callback handler or
returning a future. The Python bindings instead expose a single synchronous form
that performs the request and returns the resolved result directly, raising
SdkError (or RestApiError) on failure — so
the Python examples below are identical.
Example using a callback:
using Sessions = Proximie::PxRestApi::Sessions;
using SessionsResult = ProximieServices::PayloadResult<Sessions>;
pxapi.getSessions(
[](const SessionsResult& result)
{
if (result.error) {
// Handle error
} else {
// The result contains a response with a typed payload
std::shared_ptr<Sessions> sessionsPayload = result.payload;
// ...
}
});
# Unlike the C++ callback overload, the Python bindings expose the REST API in
# a single synchronous (blocking) form: get_sessions() performs the request and
# returns the resolved result directly, raising SdkError on failure.
sessions = services.get_sessions()
And using a future:
SessionsResult result = pxapi.getSessions().get(); // Note: get() will block
if (result.error) {
// Handle error
} else {
// The result contains a response with a typed payload
std::shared_ptr<Sessions> sessionsPayload = result.payload;
// ...
}
# There is likewise no separate "future" form in Python; the same blocking call
# returns the resolved Sessions payload.
sessions = services.get_sessions()
As you can see, the resulting objects are the same, but returned to the application using different approaches.
See the full SDK documentation for ProximieServices
for a list of REST API functions supported.
Error handling
As outlined above, the result value for an service call has an error,
which is a error_code value.
Depending on the type of error, there may be additional details in the response returned.
If the error was so severe that the actual end point could not be reached, only the error code will be present. If the error was in the request itself, the response will contain additional details of the response from the Proximie services.
Note
In the Python bindings a failed request raises a typed exception rather
than returning a result object. When the request reaches the service and it
returns an error response, a RestApiError (a subclass of SdkError)
is raised, carrying the same structured detail the C++ errorDetails
exposes — status_code (int), error_code (str), message and
correlation_id. When only an error code is available (for example the
endpoint could not be reached), a base SdkError is raised instead. See
Error handling for the common exception model.
auto result = pxapi.getSessions().get();
if (result.error) {
if (result.response) {
// The response is the generic data from the response
auto httpStatus = result.response->httpStatus;
auto payloadText = result.response->valueText;
//...
}
}
try:
sessions = services.get_sessions()
except RestApiError as e:
# The request reached the service but it returned an error response.
# RestApiError carries the structured error detail (status code,
# error code, correlation id, ...).
pass
except SdkError as e:
# Broader form: when the endpoint cannot be reached there are no
# error details, so a base SdkError is raised instead of RestApiError.
pass
Where errors indicate a problem with the request itself, e.g. an invalid parameter or ID,
the returned response usually has additional information, which is included in the
response’s errorDetails field.
// If there are error details, we can examine them
if (result.errorDetails) {
auto& error = result.errorDetails->error;
auto httpStatus = error.statusCode; // HTTP status code
auto errorCode = error.errorCode; // Service error code string
auto message = error.message; // Error message
// ...
}
try:
sessions = services.get_sessions()
except RestApiError as e:
status_code = e.status_code # HTTP status code (int)
error_code = e.error_code # Service error code string
message = e.message # Error message
correlation_id = e.correlation_id # Request correlation id (or None)
Finally, you can also obtain a string representation of the error using the
errorSummary()
member function. The advantage of this function is that is checks the response and
error details to produce the most informative message it can.
auto message = result.errorSummary();
std::cerr << "Error getting session: " << message << std::endl;
try:
sessions = services.get_sessions()
except SdkError as e:
print(f"Error getting session: {e}")
Could output something like:
Error getting session: [404] client error: Session aebebe1e-d9a7-47ff-8a5f-20b4f70f10a1 was not found
# Example output:
# Error getting session: [REST 404 SESSION_NOT_FOUND] Session aebebe1e-... was not found