aiida.restapi.common package#
Submodules#
Default configuration for the REST API
This file contains the exceptions that are raised by the RESTapi at the highest level, namely that of the interaction with the client. Their specificity resides into the fact that they return a message that is embedded into the HTTP response.
Example:#
…/api/v1/nodes/ … (TODO compete this with an actual example)
Other errors arising at deeper level, e.g. those raised by the QueryBuilder or internal errors, are not embedded into the HTTP response.
- exception aiida.restapi.common.exceptions.RestFeatureNotAvailable[source]#
Bases:
FeatureNotAvailable
If endpoint is not emplemented for given node type
- __annotations__ = {}#
- __module__ = 'aiida.restapi.common.exceptions'#
- exception aiida.restapi.common.exceptions.RestInputValidationError[source]#
Bases:
InputValidationError
If inputs passed in query strings are wrong
- __annotations__ = {}#
- __module__ = 'aiida.restapi.common.exceptions'#
- exception aiida.restapi.common.exceptions.RestValidationError[source]#
Bases:
ValidationError
If validation error in code.
E.g. more than one node available for given uuid
- __annotations__ = {}#
- __module__ = 'aiida.restapi.common.exceptions'#
Utility functions to work with node “full types” which are unique node identifiers.
A node’s full_type is defined as a string that uniquely defines the node type. A valid full_type is constructed by concatenating the node_type and process_type of a node with the FULL_TYPE_CONCATENATOR. Each segment of the full type can optionally be terminated by a single LIKE_OPERATOR_CHARACTER to indicate that the node_type or process_type should start with that value but can be followed by any amount of other characters. A full type is invalid if it does not contain exactly one FULL_TYPE_CONCATENATOR character. Additionally, each segment can contain at most one occurrence of the LIKE_OPERATOR_CHARACTER and it has to be at the end of the segment.
Examples of valid full types:
‘data.bool.Bool.|’ ‘process.calculation.calcfunction.%|%’ ‘process.calculation.calcjob.CalcJobNode.|aiida.calculations:arithmetic.add’ ‘process.calculation.calcfunction.CalcFunctionNode.|aiida.workflows:codtools.primitive_structure_from_cif’
Examples of invalid full types:
‘data.bool’ # Only a single segment without concatenator ‘data.|bool.Bool.|process.’ # More than one concatenator ‘process.calculation%.calcfunction.|aiida.calculations:arithmetic.add’ # Like operator not at end of segment ‘process.calculation%.calcfunction.%|aiida.calculations:arithmetic.add’ # More than one operator in segment
- class aiida.restapi.common.identifiers.Namespace(namespace, path=None, label=None, full_type=None, counter=None, is_leaf=True)[source]#
Bases:
MutableMapping
Namespace that can be used to map the node class hierarchy.
- __abstractmethods__ = frozenset({})#
- __dict__ = mappingproxy({'__module__': 'aiida.restapi.common.identifiers', '__doc__': 'Namespace that can be used to map the node class hierarchy.', 'namespace_separator': '.', 'mapping_path_to_label': {'node': 'Node', 'node.data': 'Data', 'node.process': 'Process', 'node.process.calculation': 'Calculation', 'node.process.calculation.calcjob': 'Calculation job', 'node.process.calculation.calcfunction': 'Calculation function', 'node.process.workflow': 'Workflow', 'node.process.workflow.workchain': 'Work chain', 'node.process.workflow.workfunction': 'Work function'}, 'process_full_type_mapping': {'process.calculation.calcjob.': 'process.calculation.calcjob.CalcJobNode.|aiida.calculations:{plugin_name}.%', 'process.calculation.calcfunction.': 'process.calculation.calcfunction.CalcFunctionNode.|aiida.calculations:{plugin_name}.%', 'process.workflow.workfunction.': 'process.workflow.workfunction.WorkFunctionNode.|aiida.workflows:{plugin_name}.%', 'process.workflow.workchain.': 'process.workflow.workchain.WorkChainNode.|aiida.workflows:{plugin_name}.%'}, 'process_full_type_mapping_unplugged': {'process.calculation.calcjob.': 'process.calculation.calcjob.CalcJobNode.|{plugin_name}.%', 'process.calculation.calcfunction.': 'process.calculation.calcfunction.CalcFunctionNode.|{plugin_name}.%', 'process.workflow.workfunction.': 'process.workflow.workfunction.WorkFunctionNode.|{plugin_name}.%', 'process.workflow.workchain.': 'process.workflow.workchain.WorkChainNode.|{plugin_name}.%'}, '__str__': <function Namespace.__str__>, '__init__': <function Namespace.__init__>, '_infer_full_type': <function Namespace._infer_full_type>, '__iter__': <function Namespace.__iter__>, '__len__': <function Namespace.__len__>, '__delitem__': <function Namespace.__delitem__>, '__getitem__': <function Namespace.__getitem__>, '__setitem__': <function Namespace.__setitem__>, 'is_leaf': <property object>, 'get_description': <function Namespace.get_description>, 'create_namespace': <function Namespace.create_namespace>, '__dict__': <attribute '__dict__' of 'Namespace' objects>, '__weakref__': <attribute '__weakref__' of 'Namespace' objects>, '__abstractmethods__': frozenset(), '_abc_impl': <_abc._abc_data object>, '__annotations__': {}})#
- __init__(namespace, path=None, label=None, full_type=None, counter=None, is_leaf=True)[source]#
Construct a new node class namespace.
- __module__ = 'aiida.restapi.common.identifiers'#
- __weakref__#
list of weak references to the object
- _abc_impl = <_abc._abc_data object>#
- _infer_full_type(full_type)[source]#
Infer the full type based on the current namespace path and the given full type of the leaf.
- create_namespace(name, **kwargs)[source]#
Create and return a new Namespace in this Namespace.
If the name is namespaced, the sub Namespaces will be created recursively, except if one of the namespaces is already occupied at any level by a Port in which case a ValueError will be thrown
- Parameters:
name – name (potentially namespaced) of the port to create and return
kwargs – constructor arguments that will be used only for the construction of the terminal Namespace
- Returns:
Namespace
- Raises:
ValueError if any sub namespace is occupied by a non-Namespace port
- get_description()[source]#
Return a dictionary with a description of the ports this namespace contains.
Nested PortNamespaces will be properly recursed and Ports will print their properties in a list
- Returns:
a dictionary of descriptions of the Ports contained within this PortNamespace
- property is_leaf#
- mapping_path_to_label = {'node': 'Node', 'node.data': 'Data', 'node.process': 'Process', 'node.process.calculation': 'Calculation', 'node.process.calculation.calcfunction': 'Calculation function', 'node.process.calculation.calcjob': 'Calculation job', 'node.process.workflow': 'Workflow', 'node.process.workflow.workchain': 'Work chain', 'node.process.workflow.workfunction': 'Work function'}#
- namespace_separator = '.'#
- process_full_type_mapping = {'process.calculation.calcfunction.': 'process.calculation.calcfunction.CalcFunctionNode.|aiida.calculations:{plugin_name}.%', 'process.calculation.calcjob.': 'process.calculation.calcjob.CalcJobNode.|aiida.calculations:{plugin_name}.%', 'process.workflow.workchain.': 'process.workflow.workchain.WorkChainNode.|aiida.workflows:{plugin_name}.%', 'process.workflow.workfunction.': 'process.workflow.workfunction.WorkFunctionNode.|aiida.workflows:{plugin_name}.%'}#
- process_full_type_mapping_unplugged = {'process.calculation.calcfunction.': 'process.calculation.calcfunction.CalcFunctionNode.|{plugin_name}.%', 'process.calculation.calcjob.': 'process.calculation.calcjob.CalcJobNode.|{plugin_name}.%', 'process.workflow.workchain.': 'process.workflow.workchain.WorkChainNode.|{plugin_name}.%', 'process.workflow.workfunction.': 'process.workflow.workfunction.WorkFunctionNode.|{plugin_name}.%'}#
- aiida.restapi.common.identifiers.construct_full_type(node_type, process_type)[source]#
Return the full type, which uniquely identifies any Node with the given node_type and process_type.
- Parameters:
node_type – the node_type of the Node
process_type – the process_type of the Node
- Returns:
the full type, which is a unique identifier
- aiida.restapi.common.identifiers.get_full_type_filters(full_type)[source]#
Return the QueryBuilder filters that will return all Nodes identified by the given full_type.
- Parameters:
full_type – the full_type unique node identifier
- Returns:
dictionary of filters to be passed for the filters keyword in QueryBuilder.append
- Raises:
ValueError – if the full_type is invalid
TypeError – if the full_type is not a string type
- aiida.restapi.common.identifiers.get_node_namespace(user_pk=None, count_nodes=False)[source]#
Return the full namespace of all available nodes in the current database.
- Returns:
complete node Namespace
- aiida.restapi.common.identifiers.load_entry_point_from_full_type(full_type)[source]#
Return the loaded entry point for the given full_type unique node identifier.
- Parameters:
full_type – the full_type unique node identifier
- Raises:
ValueError – if the full_type is invalid
TypeError – if the full_type is not a string type
EntryPointError – if the corresponding entry point cannot be loaded
- aiida.restapi.common.identifiers.validate_full_type(full_type)[source]#
Validate that the full_type is a valid full type unique node identifier.
- Parameters:
full_type – a Node full type
- Raises:
ValueError – if the full_type is invalid
TypeError – if the full_type is not a string type
Util methods
- class aiida.restapi.common.utils.CustomJSONProvider(app: Flask)[source]#
Bases:
DefaultJSONProvider
Custom json encoder for serialization. This has to be provided to the Flask app in order to replace the default encoder.
- __annotations__ = {}#
- __module__ = 'aiida.restapi.common.utils'#
- class aiida.restapi.common.utils.Utils(**kwargs)[source]#
Bases:
object
A class that gathers all the utility functions for parsing URI, validating request, pass it to the translator, and building HTTP response
An istance of Utils has to be included in the api class so that the configuration parameters used to build the api are automatically accessible by the methods of Utils without the need to import them from the config.py file.
- __dict__ = mappingproxy({'__module__': 'aiida.restapi.common.utils', '__doc__': 'A class that gathers all the utility functions for parsing URI,\n validating request, pass it to the translator, and building HTTP response\n\n An istance of Utils has to be included in the api class so that the\n configuration parameters used to build the api are automatically\n accessible by the methods of Utils without the need to import them from\n the config.py file.\n\n ', 'op_conv_map': {'=': '==', '!=': '!==', '=in=': 'in', '=notin=': '!in', '>': '>', '<': '<', '>=': '>=', '<=': '<=', '=like=': 'like', '=ilike=': 'ilike'}, '__init__': <function Utils.__init__>, 'strip_api_prefix': <function Utils.strip_api_prefix>, 'split_path': <staticmethod(<function Utils.split_path>)>, 'parse_path': <function Utils.parse_path>, 'validate_request': <function Utils.validate_request>, 'paginate': <function Utils.paginate>, 'build_headers': <function Utils.build_headers>, 'build_response': <staticmethod(<function Utils.build_response>)>, 'build_datetime_filter': <staticmethod(<function Utils.build_datetime_filter>)>, 'build_translator_parameters': <function Utils.build_translator_parameters>, 'parse_query_string': <function Utils.parse_query_string>, '__dict__': <attribute '__dict__' of 'Utils' objects>, '__weakref__': <attribute '__weakref__' of 'Utils' objects>, '__annotations__': {}})#
- __module__ = 'aiida.restapi.common.utils'#
- __weakref__#
list of weak references to the object
- static build_datetime_filter(dtobj)[source]#
This function constructs a filter for a datetime object to be in a certain datetime interval according to the precision.
The interval is [reference_datetime, reference_datetime + delta_time], where delta_time is a function fo the required precision.
This function should be used to replace a datetime filter based on the equality operator that is inehrently “picky” because it implies matching two datetime objects down to the microsecond or something, by a “tolerant” operator which checks whether the datetime is in an interval.
- Returns:
a suitable entry of the filter dictionary
- build_headers(rel_pages=None, url=None, total_count=None)[source]#
Construct the header dictionary for an HTTP response. It includes related pages, total count of results (before pagination).
- Parameters:
rel_pages – a dictionary defining related pages (first, prev, next, last)
url – (string) the full url, i.e. the url that the client uses to get Rest resources
- static build_response(status=200, headers=None, data=None)[source]#
Build the response
- Parameters:
status – status of the response, e.g. 200=OK, 400=bad request
headers – dictionary for additional header k,v pairs, e.g. X-total-count=<number of rows resulting from query>
data – a dictionary with the data returned by the Resource
- Returns:
a Flask response object
- build_translator_parameters(field_list)[source]#
Takes a list of elements resulting from the parsing the query_string and elaborates them in order to provide translator-compliant instructions
- Parameters:
field_list – a (nested) list of elements resulting from parsing the query_string
- Returns:
the filters in the
- op_conv_map = {'!=': '!==', '<': '<', '<=': '<=', '=': '==', '=ilike=': 'ilike', '=in=': 'in', '=like=': 'like', '=notin=': '!in', '>': '>', '>=': '>='}#
- paginate(page, perpage, total_count)[source]#
Calculates limit and offset for the reults of a query, given the page and the number of restuls per page. Moreover, calculates the last available page and raises an exception if the required page exceeds that limit. If number of rows==0, only page 1 exists :param page: integer number of the page that has to be viewed :param perpage: integer defining how many results a page contains :param total_count: the total number of rows retrieved by the query :return: integers: limit, offset, rel_pages
- parse_path(path_string, parse_pk_uuid=None)[source]#
Takes the path and parse it checking its validity. Does not parse “io”, “content” fields. I do not check the validity of the path, since I assume that this is done by the Flask routing methods.
- Parameters:
path_string – the path string
parse_id_uuid – if ‘pk’ (‘uuid’) expects an integer (uuid starting pattern)
- Returns:
resource_type (string) page (integer) node_id (string: uuid starting pattern, int: pk) query_type (string))
- parse_query_string(query_string)[source]#
Function that parse the querystring, extracting infos for limit, offset, ordering, filters, attribute and extra projections. :param query_string (as obtained from request.query_string) :return: parsed values for the querykeys
- static split_path(path)[source]#
- Parameters:
path – entire path contained in flask request
- Returns:
list of each element separated by ‘/’
- strip_api_prefix(path)[source]#
Removes the PREFIX from an URL path. PREFIX must be defined in the config.py file:
PREFIX = "/api/v2" path = "/api/v2/calculations/page/2" strip_api_prefix(path) ==> "/calculations/page/2"
- Parameters:
path – the URL path string
- Returns:
the same URL without the prefix
- validate_request(limit=None, offset=None, perpage=None, page=None, query_type=None, is_querystring_defined=False)[source]#
Performs various checks on the consistency of the request. Add here all the checks that you want to do, except validity of the page number that is done in paginate(). Future additional checks must be added here
- aiida.restapi.common.utils.close_thread_connection(wrapper=None, enabled=None, adapter=None, proxy=<class 'FunctionWrapper'>)[source]#
Close the profile’s storage connection, for the current thread.
This decorator can be used for router endpoints. It is needed due to the server running in threaded mode, i.e., creating a new thread for each incoming request, and leaving connections unreleased.
Note, this is currently hard-coded to the PsqlDosBackend storage backend.