schemadiff.validation_rules API documentation

# Module `schemadiff.validation_rules`

Expand source code

    import typing
    from abc import ABC, abstractmethod
    from typing import List, Set

    from graphql import GraphQLObjectType

    from schemadiff.changes.enum import EnumValueAdded, EnumValueDescriptionChanged
    from schemadiff.changes.field import FieldDescriptionChanged, FieldArgumentAdded
    from schemadiff.changes.object import ObjectTypeFieldAdded
    from schemadiff.changes.type import AddedType, TypeDescriptionChanged

    class ValidationRule(ABC):
        """Abstract class for creating schema Validation Rules."""
        name: str = ""

        def __init__(self, change):
            self.change = change

        @abstractmethod
        def is_valid(self) -> bool:
            """Evaluate the change regarding the rule class defined.

            Returns:
                bool: True if change is valid, False otherwise
            """

        @property
        @abstractmethod
        def message(self) -> str:
            """Formatted change message"""

        @classmethod
        def get_subclasses_by_names(cls, names: List[str]) -> Set[typing.Type['ValidationRule']]:
            if not names:
                return set()
            return {subclass for subclass in cls.__subclasses__() if subclass.name in names}

        @classmethod
        def get_rules_list(cls) -> Set[str]:
            return {subclass.name for subclass in cls.__subclasses__()}

    class AddTypeWithoutDescription(ValidationRule):
        """Restrict adding new GraphQL types without entering
        a non-empty description."""
        name = "add-type-without-description"

        def is_valid(self) -> bool:
            EMPTY = (None, "")
            if not isinstance(self.change, AddedType):
                return True

            added_type = self.change.type
            type_has_description = added_type.description not in EMPTY

            if isinstance(added_type, GraphQLObjectType):
                all_its_fields_have_description = all(
                    field.description not in EMPTY
                    for field in added_type.fields.values()
                )
                return type_has_description and all_its_fields_have_description
            else:
                return type_has_description

        @property
        def message(self):
            return f"{self.change.message} without a description for {self.change.path} (rule: `{self.name}`)."

    class RemoveTypeDescription(ValidationRule):
        """Restrict removing the description from an existing
        GraphQL type."""
        name = "remove-type-description"

        def is_valid(self) -> bool:
            if isinstance(self.change, TypeDescriptionChanged):
                return self.change.new_desc not in (None, "")
            return True

        @property
        def message(self):
            return (
                f"Description for type `{self.change.type}` was "
                f"removed (rule: `{self.name}`)"
            )

    class AddFieldWithoutDescription(ValidationRule):
        """Restrict adding fields without description."""
        name = "add-field-without-description"

        def is_valid(self) -> bool:
            if isinstance(self.change, ObjectTypeFieldAdded):
                return self.change.description not in (None, "")
            return True

        @property
        def message(self):
            return f"{self.change.message} without a description for {self.change.path} (rule: `{self.name}`)."

    class RemoveFieldDescription(ValidationRule):
        """Restrict removing field description."""
        name = "remove-field-description"

        def is_valid(self) -> bool:
            if isinstance(self.change, FieldDescriptionChanged):
                return self.change.new_field.description not in (None, "")
            return True

        @property
        def message(self):
            return (
                f"`{self.change.type.name}.{self.change.field_name}` description was "
                f"removed (rule: `{self.name}`)"
            )

    class AddEnumValueWithoutDescription(ValidationRule):
        """Restrict adding enum value without description."""
        name = "add-enum-value-without-description"

        def is_valid(self) -> bool:
            if isinstance(self.change, EnumValueAdded):
                return self.change.description not in (None, "")
            return True

        @property
        def message(self):
            return f"{self.change.message} without a description (rule: `{self.name}`)"

    class RemoveEnumValueDescription(ValidationRule):
        """Restrict adding enum value without description."""
        name = "remove-enum-value-description"

        def is_valid(self) -> bool:
            if isinstance(self.change, EnumValueDescriptionChanged):
                return self.change.new_value.description not in (None, "")
            return True

        @property
        def message(self):
            return f"Description for enum value `{self.change.name}` was removed (rule: `{self.name}`)"

## Classes

`class AddEnumValueWithoutDescription (change)`

  Restrict adding enum value without description.

  Expand source code

      class AddEnumValueWithoutDescription(ValidationRule):
          """Restrict adding enum value without description."""
          name = "add-enum-value-without-description"

          def is_valid(self) -> bool:
              if isinstance(self.change, EnumValueAdded):
                  return self.change.description not in (None, "")
              return True

          @property
          def message(self):
              return f"{self.change.message} without a description (rule: `{self.name}`)"

  ### Ancestors

  - ValidationRule
  - abc.ABC

  ### Class variables

  `var name : str`

  ### Inherited members

  - `ValidationRule`:
    - `is_valid`
    - `message`
`class AddFieldWithoutDescription (change)`

  Restrict adding fields without description.

  Expand source code

      class AddFieldWithoutDescription(ValidationRule):
          """Restrict adding fields without description."""
          name = "add-field-without-description"

          def is_valid(self) -> bool:
              if isinstance(self.change, ObjectTypeFieldAdded):
                  return self.change.description not in (None, "")
              return True

          @property
          def message(self):
              return f"{self.change.message} without a description for {self.change.path} (rule: `{self.name}`)."

  ### Ancestors

  - ValidationRule
  - abc.ABC

  ### Class variables

  `var name : str`

  ### Inherited members

  - `ValidationRule`:
    - `is_valid`
    - `message`
`class AddTypeWithoutDescription (change)`

  Restrict adding new GraphQL types without entering a non-empty description.

  Expand source code

      class AddTypeWithoutDescription(ValidationRule):
          """Restrict adding new GraphQL types without entering
          a non-empty description."""
          name = "add-type-without-description"

          def is_valid(self) -> bool:
              EMPTY = (None, "")
              if not isinstance(self.change, AddedType):
                  return True

              added_type = self.change.type
              type_has_description = added_type.description not in EMPTY

              if isinstance(added_type, GraphQLObjectType):
                  all_its_fields_have_description = all(
                      field.description not in EMPTY
                      for field in added_type.fields.values()
                  )
                  return type_has_description and all_its_fields_have_description
              else:
                  return type_has_description

          @property
          def message(self):
              return f"{self.change.message} without a description for {self.change.path} (rule: `{self.name}`)."

  ### Ancestors

  - ValidationRule
  - abc.ABC

  ### Class variables

  `var name : str`

  ### Inherited members

  - `ValidationRule`:
    - `is_valid`
    - `message`
`class RemoveEnumValueDescription (change)`

  Restrict adding enum value without description.

  Expand source code

      class RemoveEnumValueDescription(ValidationRule):
          """Restrict adding enum value without description."""
          name = "remove-enum-value-description"

          def is_valid(self) -> bool:
              if isinstance(self.change, EnumValueDescriptionChanged):
                  return self.change.new_value.description not in (None, "")
              return True

          @property
          def message(self):
              return f"Description for enum value `{self.change.name}` was removed (rule: `{self.name}`)"

  ### Ancestors

  - ValidationRule
  - abc.ABC

  ### Class variables

  `var name : str`

  ### Inherited members

  - `ValidationRule`:
    - `is_valid`
    - `message`
`class RemoveFieldDescription (change)`

  Restrict removing field description.

  Expand source code

      class RemoveFieldDescription(ValidationRule):
          """Restrict removing field description."""
          name = "remove-field-description"

          def is_valid(self) -> bool:
              if isinstance(self.change, FieldDescriptionChanged):
                  return self.change.new_field.description not in (None, "")
              return True

          @property
          def message(self):
              return (
                  f"`{self.change.type.name}.{self.change.field_name}` description was "
                  f"removed (rule: `{self.name}`)"
              )

  ### Ancestors

  - ValidationRule
  - abc.ABC

  ### Class variables

  `var name : str`

  ### Inherited members

  - `ValidationRule`:
    - `is_valid`
    - `message`
`class RemoveTypeDescription (change)`

  Restrict removing the description from an existing GraphQL type.

  Expand source code

      class RemoveTypeDescription(ValidationRule):
          """Restrict removing the description from an existing
          GraphQL type."""
          name = "remove-type-description"

          def is_valid(self) -> bool:
              if isinstance(self.change, TypeDescriptionChanged):
                  return self.change.new_desc not in (None, "")
              return True

          @property
          def message(self):
              return (
                  f"Description for type `{self.change.type}` was "
                  f"removed (rule: `{self.name}`)"
              )

  ### Ancestors

  - ValidationRule
  - abc.ABC

  ### Class variables

  `var name : str`

  ### Inherited members

  - `ValidationRule`:
    - `is_valid`
    - `message`
`class ValidationRule (change)`

  Abstract class for creating schema Validation Rules.

  Expand source code

      class ValidationRule(ABC):
          """Abstract class for creating schema Validation Rules."""
          name: str = ""

          def __init__(self, change):
              self.change = change

          @abstractmethod
          def is_valid(self) -> bool:
              """Evaluate the change regarding the rule class defined.

              Returns:
                  bool: True if change is valid, False otherwise
              """

          @property
          @abstractmethod
          def message(self) -> str:
              """Formatted change message"""

          @classmethod
          def get_subclasses_by_names(cls, names: List[str]) -> Set[typing.Type['ValidationRule']]:
              if not names:
                  return set()
              return {subclass for subclass in cls.__subclasses__() if subclass.name in names}

          @classmethod
          def get_rules_list(cls) -> Set[str]:
              return {subclass.name for subclass in cls.__subclasses__()}

  ### Ancestors

  - abc.ABC

  ### Subclasses

  - AddEnumValueWithoutDescription
  - AddFieldWithoutDescription
  - AddTypeWithoutDescription
  - RemoveEnumValueDescription
  - RemoveFieldDescription
  - RemoveTypeDescription

  ### Class variables

  `var name : str`

  ### Static methods

  `def get_rules_list() ‑> Set[str]`
    Expand source code

        @classmethod
        def get_rules_list(cls) -> Set[str]:
            return {subclass.name for subclass in cls.__subclasses__()}

  `def get_subclasses_by_names(names: List[str]) ‑> Set[Type[ValidationRule]]`
    Expand source code

        @classmethod
        def get_subclasses_by_names(cls, names: List[str]) -> Set[typing.Type['ValidationRule']]:
            if not names:
                return set()
            return {subclass for subclass in cls.__subclasses__() if subclass.name in names}

  ### Instance variables

  `var message : str`

    Formatted change message

    Expand source code

        @property
        @abstractmethod
        def message(self) -> str:
            """Formatted change message"""

  ### Methods

  `def is_valid(self) ‑> bool`

    Evaluate the change regarding the rule class defined.

    ## Returns

    `bool`
      True if change is valid, False otherwise
    Expand source code

        @abstractmethod
        def is_valid(self) -> bool:
            """Evaluate the change regarding the rule class defined.

            Returns:
                bool: True if change is valid, False otherwise
            """