Skip to main content

Example usage

The API class can be instantiated using VIKTOR's api_v1 module:

from viktor.api_v1 import API

api = API(
environment="cloud.viktor.ai", # only needed when using outside the context of the VIKTOR platform
token=os.environ["token"], # only needed when using outside the context of the current workspace
)

Obtaining entity data

When using the API from an external source, or want to obtain data from a different workspace than the current, you'll need to start at the workspace data layer:

  • Get all workspaces that you have access to: workspaces = api.get_workspaces()
  • Start with a given workspace id: workspace = api.get_workspace(workspace_id)

Once you have obtained the correct workspace, you can obtain an entity/entities:

  • Start from the root entities: workspace.get_root_entities()
  • Get all entities of a given type: workspace.get_entities_by_type('MyType')
  • Start with a given entity id: workspace.get_entity(entity_id)

When using the API to obtain data from the current workspace, you can use the following shorthand notation to obtain an entity/entities (the current workspace is automatically selected):

  • Start from the root entities: api.get_root_entities()
  • Get all entities of a given type: api.get_entities_by_type('MyType')
  • Start with a given entity id: api.get_entity(entity_id)

Next to 'getting' the entity by its id, it is also possible to navigate to relatives (get_entity_children, get_entity_siblings, get_entity_parent), modify the corresponding entity (rename_entity, delete_entity, set_entity_params), get its history (get_entity_revisions), get the corresponding file (get_entity_file), or create a child entity (create_child_entity), directly by using the API methods.

For convenience, the Entity object itself also provides (most of) above methods. So it is also possible to navigate by:

entity.parent()
entity.children()
entity.siblings()

It's possible to chain these methods, e.g.:

grand_parent = entity.parent().parent()

And filter on entity type:

gef_children = entity.children(entity_type_names=['GEF'])

In case only the id, name or type of an Entity is required, the performance of the API call(s) can be enhanced by setting the include_params flag to False:

gef_children = entity.children(entity_type_names=['GEF'], include_params=False)

The params of an Entity can simply be obtained by calling last_saved_params. In a similar way, the summary can be obtained by calling last_saved_summary:

params = entity.last_saved_params
summary = entity.last_saved_summary

Current workspace and entity

The workspace_id of the current workspace, and entity_id and entity_name of the current entity are sent along within all view-methods and callback-functions, for convenience. The workspace_id / entity_id can be used to construct the current workspace/entity object, from which you can navigate to relatives. For example, within a view-method:

from viktor.api_v1 import API

@DataView("Data", duration_guess=1)
def calculate_view(self, params, entity_id, entity_name, workspace_id, **kwargs):
current_entity = API().get_entity(entity_id)
parent = current_entity.parent()
parent_params = parent.last_saved_params
# do something with parent_params
return DataResult(...)

Or a callback-function in the parametrization:

from viktor.api_v1 import API
from viktor.parametrization import ViktorParametrization, OptionField


def get_options(params, entity_id, entity_name, workspace_id, **kwargs):
current_entity = API().get_entity(entity_id)
parent = current_entity.parent()
parent_params = parent.last_saved_params
# do something with parent_params
return [...]


class Parametrization(ViktorParametrization):
field = OptionField(..., options=get_options)

Within a single job (see VIKTOR's call flow for the possible triggers of a job) the results of API calls are temporarily stored (memoized). This means that multiple calls to the same entity only require a single request in the background. This is particularly handy for option functions:

from viktor.api_v1 import API

def get_options1_from_parent(params, entity_id, workspace_id, **kwargs):
parent = API().get_entity(entity_id).parent()
parent_params = parent.last_saved_params
# get options1 from parent_params
...

def get_options2_from_parent(params, entity_id, workspace_id, **kwargs):
parent = API().get_entity(entity_id).parent()
parent_params = parent.last_saved_params
# get options2 from parent_params
...

class Parametrization(ViktorParametrization):
option1 = OptionField('Option 1', options=get_options1_from_parent)
option2 = OptionField('Option 2', options=get_options2_from_parent)

List-object

Some methods return a series of objects in a list-object, instead of an actual list of instances (e.g. EntityList, instead of List[Entity]). This has been done for performance reasons and has little effect on handling, as most normal list operations are still allowed:

children = entity.children()  # children is of type `EntityList`

len(children) # ok
children[0] # positive indexing: ok
children[-1] # negative indexing: ok
for child in children: # iterating: ok
# do something with child
note

Slicing of a list-object (e.g. children[0:5]) is not supported.

Bypassing user access restrictions

caution

If not used properly, (confidential) information which SHOULD NOT be accessible to a user may leak (e.g. by including confidential data in a view, the data is visible to users with access to that view). Please consider the following when using this flag:

  • Make sure the app's admin is aware that the code circumvents certain permissions at specific places.
  • Make sure to test the implementation thoroughly, to ensure no confidential data is leaked.

Some applications have user restrictions that limit access to certain entities or entity types. The code of an application inherits the permissions of the user currently running it. This means that, for example, if a user does not have permission to read some entity data, the code will also NOT be able to reach this data through an API call when run by the same user.

In some cases it is desirable to restrict user access to a certain entity but have access within the code nonetheless. An example is the case of a global settings entity that consists of confidential pricing data, which should be inaccessible for users but accessible for the code to calculate costs. For these cases a privileged flag can be used:

  1. Configure viktor.config.toml to make use of the privileged api. This serves as an additional layer of security/awareness.

    enable_privileged_api = true
  2. Use the privileged flag on the relevant API methods.

    from viktor.api_v1 import API
    ...
    settings_entity = API().get_root_entities(entity_type_names=['Settings'], privileged=True)[0]
    settings = settings_entity.last_saved_params