Skip to content

nautobot.apps.graphql

GraphQL API for Nautobot.

nautobot.apps.graphql.BigInteger

Bases: Scalar

An integer which, unlike GraphQL's native Int type, doesn't reject values outside (-2^31, 2^31-1).

Currently only used for ASNField, which goes up to 2^32-1 (i.e., unsigned 32-bit int); it's possible that this approach may fail for values in excess of 2^53-1 (the largest integer value supported in JavaScript).

Source code in nautobot/core/graphql/__init__.py
class BigInteger(Scalar):
    """An integer which, unlike GraphQL's native Int type, doesn't reject values outside (-2^31, 2^31-1).

    Currently only used for ASNField, which goes up to 2^32-1 (i.e., unsigned 32-bit int); it's possible
    that this approach may fail for values in excess of 2^53-1 (the largest integer value supported in JavaScript).
    """

    serialize = int
    parse_value = int

    @staticmethod
    def parse_literal(node):
        if isinstance(node, ast.IntValue):
            return int(node.value)
        return None

nautobot.apps.graphql.ContentTypeType

Bases: OptimizedNautobotObjectType

Graphene-Django object type for ContentType records.

Needed because ContentType is a built-in model, not one that we own and can auto-generate types for.

Source code in nautobot/core/graphql/types.py
class ContentTypeType(OptimizedNautobotObjectType):
    """
    Graphene-Django object type for ContentType records.

    Needed because ContentType is a built-in model, not one that we own and can auto-generate types for.
    """

    class Meta:
        model = ContentType

nautobot.apps.graphql.construct_resolver(model_name, resolver_type)

Constructs a resolve_[cable_peer|connected_endpoint]_ function for a given model type.

Parameters:

Name Type Description Default
model_name str

Name of the model to construct a resolver function for (e.g. CircuitTermination).

required
resolver_type str

One of ['connected_endpoint', 'cable_peer']

required
Source code in nautobot/core/graphql/utils.py
def construct_resolver(model_name, resolver_type):
    """Constructs a resolve_[cable_peer|connected_endpoint]_<endpoint> function for a given model type.

    Args:
        model_name (str): Name of the model to construct a resolver function for (e.g. CircuitTermination).
        resolver_type (str): One of ['connected_endpoint', 'cable_peer']
    """
    if resolver_type == "cable_peer":

        def resolve_cable_peer(self, args):
            peer = self.get_cable_peer()
            if type(peer).__name__ == model_name:
                return peer
            return None

        return resolve_cable_peer

    if resolver_type == "connected_endpoint":

        def resolve_connected_endpoint(self, args):
            peer = self.connected_endpoint
            if type(peer).__name__ == model_name:
                return peer
            return None

        return resolve_connected_endpoint

    raise ValueError(f"resolver_type must be 'cable_peer' or 'connected_endpoint', not '{resolver_type}'")

nautobot.apps.graphql.execute_query(query, variables=None, request=None, user=None)

Execute a query from the ORM.

Parameters:

Name Type Description Default
query str

String with GraphQL query.

required
variables dict

If the query has variables they need to be passed in as a dictionary.

None
request django.test.client.RequestFactory

Used to authenticate.

None
user django.contrib.auth.models.User

Used to authenticate.

None

Returns:

Type Description
GraphQLDocument

Result for query

Source code in nautobot/core/graphql/__init__.py
def execute_query(query, variables=None, request=None, user=None):
    """Execute a query from the ORM.

    Args:
        query (str): String with GraphQL query.
        variables (dict, optional): If the query has variables they need to be passed in as a dictionary.
        request (django.test.client.RequestFactory, optional): Used to authenticate.
        user (django.contrib.auth.models.User, optional): Used to authenticate.

    Returns:
        (GraphQLDocument): Result for query
    """
    if not request and not user:
        raise ValueError("Either request or username should be provided")
    if not request:
        request = RequestFactory().post("/graphql/")
        request.user = user
    backend = get_default_backend()
    schema = graphene_settings.SCHEMA
    document = backend.document_from_string(schema, query)
    if variables:
        return document.execute(context_value=request, variable_values=variables)
    else:
        return document.execute(context_value=request)

nautobot.apps.graphql.execute_saved_query(saved_query_name, **kwargs)

Execute saved query from the ORM.

Parameters:

Name Type Description Default
saved_query_name str

Name of a saved GraphQL query.

required

Other Parameters:

Name Type Description
variables Optional[dict]

If the query has variables they need to be passed in as a dictionary.

request Optional[django.test.client.RequestFactory]

Used to authenticate.

user Optional[django.contrib.auth.models.User]

Used to authenticate.

Returns:

Type Description
GraphQLDocument

Result for query

Source code in nautobot/core/graphql/__init__.py
def execute_saved_query(saved_query_name, **kwargs):
    """Execute saved query from the ORM.

    Args:
        saved_query_name (str): Name of a saved GraphQL query.

    Keyword Args:
        variables (Optional[dict]): If the query has variables they need to be passed in as a dictionary.
        request (Optional[django.test.client.RequestFactory]): Used to authenticate.
        user (Optional[django.contrib.auth.models.User]): Used to authenticate.

    Returns:
        (GraphQLDocument): Result for query
    """
    query = GraphQLQuery.objects.get(name=saved_query_name)
    return execute_query(query=query.query, **kwargs)

nautobot.apps.graphql.get_filtering_args_from_filterset(filterset_class)

Generate a list of filter arguments from a filterset.

The FilterSet class will be instantiated before extracting the list of arguments to account for dynamic filters, inserted when the class is instantiated. (required for Custom Fields filters).

Filter fields that are inheriting from BooleanFilter and NumberFilter will be converted to their appropriate type, everything else will be of type String. if the filter field is a subclass of MultipleChoiceFilter, the argument will be converted as a list

Parameters:

Name Type Description Default
filterset_class FilterSet

FilterSet class used to extract the argument

required

Returns:

Type Description
dict[graphene.Argument]

Filter Arguments organized in a dictionary

Source code in nautobot/core/graphql/utils.py
def get_filtering_args_from_filterset(filterset_class):
    """Generate a list of filter arguments from a filterset.

    The FilterSet class will be instantiated before extracting the list of arguments to
    account for dynamic filters, inserted when the class is instantiated. (required for Custom Fields filters).

    Filter fields that are inheriting from BooleanFilter and NumberFilter will be converted
    to their appropriate type, everything else will be of type String.
    if the filter field is a subclass of MultipleChoiceFilter, the argument will be converted as a list

    Args:
        filterset_class (FilterSet): FilterSet class used to extract the argument

    Returns:
        (dict[graphene.Argument]): Filter Arguments organized in a dictionary
    """

    args = {}
    instance = filterset_class()

    for filter_name, filter_field in instance.filters.items():
        # For general safety, but especially for the case of custom fields
        # (https://github.com/nautobot/nautobot/issues/464)
        # We don't have a way to map a GraphQL-sanitized filter name (such as "cf_my_custom_field") back to the
        # actual filter name (such as "cf_my-custom-field"), so if the sanitized filter name doesn't match the original
        # filter name, we just have to omit it for now. Better that than advertise a filter that doesn't actually work!
        if str_to_var_name(filter_name) != filter_name:
            logger.warning(
                'Filter "%s" on %s is not GraphQL safe, and will be omitted', filter_name, filterset_class.__name__
            )
            continue

        field_type = graphene.String
        filter_field_class = type(filter_field)

        if issubclass(filter_field_class, MultiValueBigNumberFilter):
            field_type = graphene.List(BigInteger)
        elif issubclass(filter_field_class, (MultiValueFloatFilter, MultiValueDecimalFilter)):
            field_type = graphene.List(graphene.Float)
        elif issubclass(filter_field_class, MultiValueNumberFilter):
            field_type = graphene.List(graphene.Int)
        else:
            if issubclass(filter_field_class, BooleanFilter):
                field_type = graphene.Boolean
            elif issubclass(filter_field_class, NumberFilter):
                field_type = graphene.Int
            else:
                field_type = graphene.String

            if issubclass(filter_field_class, MultipleChoiceFilter):
                field_type = graphene.List(field_type)

        args[filter_name] = graphene.Argument(
            field_type,
            description=filter_field.label,
            required=False,
        )

    # Hack to swap `type` fields to `_type` since they will conflict with
    # `graphene.types.fields.Field.type` in Graphene 2.x.
    # 2.0 TODO(jathan): Once we upgrade to Graphene 3.x we can remove this, but we
    # will still need to do an API migration to deprecate it. This argument was
    # validated to be safe to keep even in Graphene 3.
    if "type" in args:
        args["_type"] = args.pop("type")

    return args

nautobot.apps.graphql.str_to_var_name(verbose_name)

Convert a string to a variable compatible name.

Examples:

IP Addresses > ip_addresses

Source code in nautobot/core/graphql/utils.py
def str_to_var_name(verbose_name):
    """Convert a string to a variable compatible name.

    Examples:
        IP Addresses > ip_addresses
    """
    return slugify_dashes_to_underscores(verbose_name)