def find_by(cls, parent=None, **attributes):
        """
        Gets the first resource of the given type and parent (if provided) with matching attributes.
        This will trigger an api GET request.
        :param parent ResourceBase: the parent of the resource - used for nesting the request url, optional
        :param **attributes: any number of keyword arguments as attributes to search the resource by
        :returns: the matching resource, None if not found
        :raises ResourceError: if the no valid attributes are provided
        """
        all_nones = not all(attributes.values())
        if not attributes or all_nones:
            raise cls.ResourceError('at least one attribute must be provided')
        matches = cls.filter(parent, **attributes)
        if matches:
            return matches[0]
Gets the first resource of the given type and parent (if provided) with matching attributes.
        This will trigger an api GET request.
        :param parent ResourceBase: the parent of the resource - used for nesting the request url, optional
        :param **attributes: any number of keyword arguments as attributes to search the resource by
        :returns: the matching resource, None if not found
        :raises ResourceError: if the no valid attributes are provided