diff --git a/netbox/extras/graphql/filters.py b/netbox/extras/graphql/filters.py new file mode 100644 index 000000000..f5ce2b23a --- /dev/null +++ b/netbox/extras/graphql/filters.py @@ -0,0 +1,149 @@ +import strawberry +from strawberry import auto +from extras import models, filtersets + +__all__ = ( + 'ConfigContextFilter', + 'ConfigTemplateFilter', + 'CustomFieldFilter', + 'CustomFieldChoiceSetFilter', + 'CustomLinkFilter', + 'ExportTemplateFilter', + 'ImageAttachmentFilter', + 'JournalEntryFilter', + 'ObjectChangeFilter', + 'SavedFilterFilter', + 'TagFilter', + 'WebhookFilter', +) + + +@strawberry.django.filter(models.ConfigContext, lookups=True) +class ConfigContextFilter(filtersets.ConfigContextFilterSet): + id: auto + name: auto + is_active: auto + data_synced: auto + + +@strawberry.django.filter(models.ConfigTemplate, lookups=True) +class ConfigTemplateFilter(filtersets.ConfigTemplateFilterSet): + id: auto + name: auto + description: auto + data_synced: auto + + +@strawberry.django.filter(models.CustomField, lookups=True) +class CustomFieldFilter(filtersets.CustomFieldFilterSet): + id: auto + content_types: auto + name: auto + group_name: auto + required: auto + search_weight: auto + filter_logic: auto + ui_visibility: auto + weight: auto + is_cloneable: auto + description: auto + + +@strawberry.django.filter(models.CustomFieldChoiceSet, lookups=True) +class CustomFieldChoiceSetFilter(filtersets.CustomFieldChoiceSetFilterSet): + id: auto + name: auto + description: auto + base_choices: auto + order_alphabetically: auto + + +@strawberry.django.filter(models.CustomLink, lookups=True) +class CustomLinkFilter(filtersets.CustomLinkFilterSet): + id: auto + content_types: auto + name: auto + enabled: auto + link_text: auto + link_url: auto + weight: auto + group_name: auto + new_window: auto + + +@strawberry.django.filter(models.ExportTemplate, lookups=True) +class ExportTemplateFilter(filtersets.ExportTemplateFilterSet): + id: auto + content_types: auto + name: auto + description: auto + data_synced: auto + + +@strawberry.django.filter(models.ImageAttachment, lookups=True) +class ImageAttachmentFilter(filtersets.ImageAttachmentFilterSet): + id: auto + content_type_id: auto + object_id: auto + name: auto + + +@strawberry.django.filter(models.JournalEntry, lookups=True) +class JournalEntryFilter(filtersets.JournalEntryFilterSet): + id: auto + assigned_object_type_id: auto + assigned_object_id: auto + created: auto + kind: auto + + +@strawberry.django.filter(models.ObjectChange, lookups=True) +class ObjectChangeFilter(filtersets.ObjectChangeFilterSet): + id: auto + user: auto + user_name: auto + request_id: auto + action: auto + changed_object_type_id: auto + changed_object_id: auto + object_repr: auto + + +@strawberry.django.filter(models.SavedFilter, lookups=True) +class SavedFilterFilter(filtersets.SavedFilterFilterSet): + id: auto + content_types: auto + name: auto + slug: auto + description: auto + enabled: auto + shared: auto + weight: auto + + +@strawberry.django.filter(models.Tag, lookups=True) +class TagFilter(filtersets.TagFilterSet): + id: auto + name: auto + slug: auto + # color: auto + description: auto + object_types: auto + + +@strawberry.django.filter(models.Webhook, lookups=True) +class WebhookFilter(filtersets.WebhookFilterSet): + id: auto + name: auto + type_create: auto + type_update: auto + type_delete: auto + type_job_start: auto + type_job_end: auto + payload_url: auto + enabled: auto + http_method: auto + http_content_type: auto + secret: auto + ssl_verification: auto + ca_file_path: auto diff --git a/netbox/extras/graphql/mixins.py b/netbox/extras/graphql/mixins.py index ac535c99d..78b8b0ff8 100644 --- a/netbox/extras/graphql/mixins.py +++ b/netbox/extras/graphql/mixins.py @@ -1,7 +1,7 @@ import strawberry -import graphene +from typing import TYPE_CHECKING, Annotated, List + from django.contrib.contenttypes.models import ContentType -from graphene.types.generic import GenericScalar from extras.models import ObjectChange @@ -14,11 +14,16 @@ __all__ = ( 'TagsMixin', ) +if TYPE_CHECKING: + from .types import ImageAttachmentType, JournalEntryType, ObjectChangeType, TagType + from tenancy.graphql.types import ContactAssignmentType + +@strawberry.type class ChangelogMixin: - changelog = graphene.List('extras.graphql.types.ObjectChangeType') - def resolve_changelog(self, info): + @strawberry.django.field + def changelog(self) -> List[Annotated["ObjectChangeType", strawberry.lazy('.types')]]: content_type = ContentType.objects.get_for_model(self) object_changes = ObjectChange.objects.filter( changed_object_type=content_type, @@ -27,43 +32,49 @@ class ChangelogMixin: return object_changes.restrict(info.context.user, 'view') +@strawberry.type class ConfigContextMixin: - config_context = GenericScalar() - def resolve_config_context(self, info): + @strawberry.django.field + def config_context(self) -> strawberry.scalars.JSON: return self.get_config_context() +@strawberry.type class CustomFieldsMixin: - custom_fields = GenericScalar() - def resolve_custom_fields(self, info): + @strawberry.django.field + def custom_fields(self) -> strawberry.scalars.JSON: return self.custom_field_data +@strawberry.type class ImageAttachmentsMixin: - image_attachments = graphene.List('extras.graphql.types.ImageAttachmentType') - def resolve_image_attachments(self, info): + @strawberry.django.field + def image_attachments(self) -> List[Annotated["ImageAttachmentType", strawberry.lazy('.types')]]: return self.images.restrict(info.context.user, 'view') +@strawberry.type class JournalEntriesMixin: - journal_entries = graphene.List('extras.graphql.types.JournalEntryType') - def resolve_journal_entries(self, info): + @strawberry.django.field + def journal_entries(self) -> List[Annotated["JournalEntryType", strawberry.lazy('.types')]]: return self.journal_entries.restrict(info.context.user, 'view') +@strawberry.type class TagsMixin: - tags = graphene.List('extras.graphql.types.TagType') - def resolve_tags(self, info): + @strawberry.django.field + def tags(self) -> List[Annotated["TagType", strawberry.lazy('.types')]]: return self.tags.all() +@strawberry.type class ContactsMixin: - contacts = graphene.List('tenancy.graphql.types.ContactAssignmentType') - def resolve_contacts(self, info): + @strawberry.django.field + def contacts(self) -> List[Annotated["ContactAssignmentType", strawberry.lazy('tenancy.graphql.types')]]: return list(self.contacts.all()) diff --git a/netbox/extras/graphql/types.py b/netbox/extras/graphql/types.py index 068da02f2..e962859db 100644 --- a/netbox/extras/graphql/types.py +++ b/netbox/extras/graphql/types.py @@ -1,6 +1,10 @@ +import strawberry +from strawberry import auto + from extras import filtersets, models from extras.graphql.mixins import CustomFieldsMixin, TagsMixin from netbox.graphql.types import BaseObjectType, ObjectType, OrganizationalObjectType +from .filters import * __all__ = ( 'ConfigContextType', @@ -18,97 +22,109 @@ __all__ = ( ) +@strawberry.django.type( + models.ConfigContext, + fields='__all__', + filters=ConfigContextFilter +) class ConfigContextType(ObjectType): - - class Meta: - model = models.ConfigContext - fields = '__all__' - filterset_class = filtersets.ConfigContextFilterSet + pass +@strawberry.django.type( + models.ConfigTemplate, + fields='__all__', + filters=ConfigTemplateFilter +) class ConfigTemplateType(TagsMixin, ObjectType): - - class Meta: - model = models.ConfigTemplate - fields = '__all__' - filterset_class = filtersets.ConfigTemplateFilterSet + pass +@strawberry.django.type( + models.CustomField, + fields='__all__', + filters=CustomFieldFilter +) class CustomFieldType(ObjectType): - - class Meta: - model = models.CustomField - exclude = ('content_types', ) - filterset_class = filtersets.CustomFieldFilterSet + pass +@strawberry.django.type( + models.CustomFieldChoiceSet, + fields='__all__', + filters=CustomFieldChoiceSetFilter +) class CustomFieldChoiceSetType(ObjectType): - - class Meta: - model = models.CustomFieldChoiceSet - fields = '__all__' - filterset_class = filtersets.CustomFieldChoiceSetFilterSet + pass +@strawberry.django.type( + models.CustomLink, + fields='__all__', + filters=CustomLinkFilter +) class CustomLinkType(ObjectType): - - class Meta: - model = models.CustomLink - exclude = ('content_types', ) - filterset_class = filtersets.CustomLinkFilterSet + pass +@strawberry.django.type( + models.ExportTemplate, + fields='__all__', + filters=ExportTemplateFilter +) class ExportTemplateType(ObjectType): - - class Meta: - model = models.ExportTemplate - exclude = ('content_types', ) - filterset_class = filtersets.ExportTemplateFilterSet + pass +@strawberry.django.type( + models.ImageAttachment, + fields='__all__', + filters=ImageAttachmentFilter +) class ImageAttachmentType(BaseObjectType): - - class Meta: - model = models.ImageAttachment - fields = '__all__' - filterset_class = filtersets.ImageAttachmentFilterSet + pass +@strawberry.django.type( + models.JournalEntry, + fields='__all__', + filters=JournalEntryFilter +) class JournalEntryType(CustomFieldsMixin, TagsMixin, ObjectType): - - class Meta: - model = models.JournalEntry - fields = '__all__' - filterset_class = filtersets.JournalEntryFilterSet + pass +@strawberry.django.type( + models.ObjectChange, + fields='__all__', + filters=ObjectChangeFilter +) class ObjectChangeType(BaseObjectType): - - class Meta: - model = models.ObjectChange - fields = '__all__' - filterset_class = filtersets.ObjectChangeFilterSet + pass +@strawberry.django.type( + models.SavedFilter, + exclude=['content_types',], + filters=SavedFilterFilter +) class SavedFilterType(ObjectType): - - class Meta: - model = models.SavedFilter - exclude = ('content_types', ) - filterset_class = filtersets.SavedFilterFilterSet + pass +@strawberry.django.type( + models.Tag, + exclude=['extras_taggeditem_items', 'color'], # bug - remove color from exclude + filters=TagFilter +) class TagType(ObjectType): - - class Meta: - model = models.Tag - exclude = ('extras_taggeditem_items',) - filterset_class = filtersets.TagFilterSet + pass +@strawberry.django.type( + models.Webhook, + exclude=['content_types',], + filters=WebhookFilter +) class WebhookType(OrganizationalObjectType): - - class Meta: - model = models.Webhook - exclude = ('content_types', ) - filterset_class = filtersets.WebhookFilterSet + pass diff --git a/netbox/netbox/graphql/types.py b/netbox/netbox/graphql/types.py index ed466b21a..56785c829 100644 --- a/netbox/netbox/graphql/types.py +++ b/netbox/netbox/graphql/types.py @@ -21,23 +21,24 @@ __all__ = ( # Base types # +@strawberry.type class BaseObjectType: """ Base GraphQL object type for all NetBox objects. Restricts the model queryset to enforce object permissions. """ - display: auto - class_type: auto @classmethod def get_queryset(cls, queryset, info): # Enforce object permissions on the queryset return queryset.restrict(info.context.request.user, 'view') - def resolve_display(parent, info, **kwargs): - return str(parent) + @strawberry.django.field + def display(self) -> str: + return str(self) - def resolve_class_type(parent, info, **kwargs): - return parent.__class__.__name__ + @strawberry.django.field + def class_type(self) -> str: + return self.__class__.__name__ class ObjectType( @@ -79,8 +80,9 @@ class NetBoxObjectType( # Miscellaneous types # +@strawberry.django.type( + ContentType, + fields=['id', 'app_label', 'model'], +) class ContentTypeType: - - class Meta: - model = ContentType - fields = ('id', 'app_label', 'model') + pass