class AssetTagAssignment(BaseModel):

    def __init__(
            self,
            value_uuid=None,
            category_name=None,
            container_uuid=None,
            asset_uuid=None,
            created_at=None,
            source=None,
            value=None,
            created_by=None,
            category_uuid=None
    ):
        self.value_uuid = value_uuid
        self.category_name = category_name
        self.container_uuid = container_uuid
        self.asset_uuid = asset_uuid
        self.created_at = created_at
        self.source = source
        self.value = value
        self.created_by = created_by
        self.category_uuid = category_uuid class AssetTagAssignmentList(BaseModel):

    def __init__(
            self,
            tags=None
    ):
        self._tags = None
        self.tags = tags

    @property
    def tags(self):
        return self._tags

    @tags.setter
    @BaseModel._model_list(AssetTagAssignment)
    def tags(self, tags):
        self._tags = tags class TagCategory(BaseModel):

    def __init__(
            self,
            container_uuid=None,
            uuid=None,
            name=None,
            description=None,
            created_at=None,
            created_by=None,
            updated_at=None,
            updated_by=None,
            reserved=None,
            model_name=None
    ):
        self.container_uuid = container_uuid
        self.uuid = uuid
        self.name = name
        self.description = description
        self.created_at = created_at
        self.created_by = created_by
        self.updated_at = updated_at
        self.updated_by = updated_by
        self.reserved = reserved
        self.model_name = model_name class TagCategoryList(BaseModel):

    def __init__(
            self,
            categories=None,
            pagination=None
    ):
        self._categories = None
        self.categories = categories
        self._pagination = None
        self.pagination = pagination

    @property
    def categories(self):
        return self._categories

    @categories.setter
    @BaseModel._model_list(TagCategory)
    def categories(self, categories):
        self._categories = categories

    @property
    def pagination(self):
        return self._pagination

    @pagination.setter
    @BaseModel._model(FilterPagination)
    def pagination(self, pagination):
        self._pagination = pagination class TagValue(BaseModel):

    def __init__(
            self,
            container_uuid=None,
            uuid=None,
            value=None,
            description=None,
            type=None,
            category_uuid=None,
            category_name=None,
            category_description=None,
            created_at=None,
            created_by=None,
            updated_at=None,
            updated_by=None,
            model_name=None,
            filters=None
    ):
        self.container_uuid = container_uuid
        self.uuid = uuid
        self.value = value
        self.description = description
        self.type = type
        self.category_uuid = category_uuid
        self.category_name = category_name
        self.category_description = category_description
        self.created_at = created_at
        self.created_by = created_by
        self.updated_at = updated_at
        self.updated_by = updated_by
        self.model_name = model_name
        self._filters = None
        self.filters = filters

    @property
    def filters(self):
        return self._filters

    @filters.setter
    @BaseModel._model(TagValueFilters)
    def filters(self, filters):
        self._filters = filters

    @classmethod
    def from_json(cls, json):
        parsed = loads(json)
        if 'filters' in parsed:
            parsed['filters'] = TagValueFilters.from_dict(dict_=parsed['filters'])
        return cls.from_dict(parsed) class TagValueFilter(BaseModel):

    OPERATOR_EQ = u'eq'
    OPERATOR_NEQ = u'neq'
    OPERATOR_MATCH = u'match'
    OPERATOR_NMATCH = u'nmatch'
    OPERATOR_SET_HAS = u'set-has'
    OPERATOR_SET_HASNOT = u'set-hasnot'
    OPERATOR_SET_HASONLY = u'set-hasonly'

    def __init__(
            self,
            field=None,
            operator=None,
            value=None
    ):
        self.field = field
        self.operator = operator
        self.value = value class TagValueFilters(BaseModel):

    OPERATOR_AND = u'and'
    OPERATOR_OR = u'or'

    ASSET_FILTER_TYPE = u'asset'

    def __init__(
            self,
            filter_type=ASSET_FILTER_TYPE,
            operator=None,
            filters=None
    ):
        self.filter_type = filter_type
        self.operator = operator
        self._filters = None
        self.filters = filters

    @property
    def filters(self):
        return self._filters


    @filters.setter
    @BaseModel._model_list(TagValueFilter)
    def filters(self, filters):
        self._filters = filters

    def as_payload(self, filter_=None):
        return {
            TagValueFilters.ASSET_FILTER_TYPE: {
                self.operator: [f.as_payload() for f in self.filters]
            }
        }

    @classmethod
    def from_dict(cls, dict_):
        inner = loads(list(dict_.values())[0])
        operator = list(inner.keys())[0]
        filters = [TagValueFilter.from_dict(f) for f in list(inner.values())[0]]
        return cls(operator=operator, filters=filters) class TagValueList(BaseModel):

    def __init__(
            self,
            values=None,
            pagination=None
    ):
        self._values = None
        self.values = values
        self._pagination = None
        self.pagination = pagination

    @property
    def values(self):
        return self._values

    @values.setter
    @BaseModel._model_list(TagValue)
    def values(self, values):
        self._values = values

    @property
    def pagination(self):
        return self._pagination

    @pagination.setter
    @BaseModel._model(FilterPagination)
    def pagination(self, pagination):
        self._pagination = pagination class AssetAssignmentsRequest(BaseRequest):

    def __init__(
            self,
            action=None,
            assets=None,
            tags=None
    ):
        """Request for TagsApi.edit_asset_tag_assignments.

        :param action: The action to be performed. Can have a value of **add** or **remove**.
        :type action: string
        :param assets: The list of asset identifiers to be updated
        :type assets: list
        :param tags: The list of tag values to update assets with
        :type tags: list
        """
        self.action = action
        self.assets = assets
        self.tags = tags class TagCategoryRequest(BaseRequest):

    def __init__(
            self,
            name,
            description=None,
    ):
        """Request for TagsApi.create_category and TagsApi.edit_category.

        :param name: The name of the tag category.
        :type name: string
        :param description: The description of the tag category.
        :type description: string
        """
        self.name = name
        self.description = description class TagValueRequest(BaseRequest):

    def __init__(
            self,
            value,
            category_uuid=None,
            category_name=None,
            category_description=None,
            description=None,
            filters=None
    ):
        """Request for TagsApi.create_value and TagsApi.edit_value.

        :param category_uuid: The uuid of category to add value to
        :type category_uuid: string
        :param category_name: The category name
        :type category_name: string
        :param category_description: The category description
        :type category_description: string
        :param value: The value to be added
        :type value: string
        :param description: The description of the tag value.
        :type description: string
        :param filters: Optional filters to create a dynamic tag
        :type filters: TagValueFilters
        """
        self.category_uuid = category_uuid
        self.category_name = category_name
        self.category_description = category_description
        self.value = value
        self.description = description
        if filters is not None:
            assert filters.operator in [
                TagValueFilters.OPERATOR_AND,
                TagValueFilters.OPERATOR_OR
            ]
            self.filters = filters.as_payload()
import pytest
from random import randint
from tenable_io.api.models import AssetTagAssignment, AssetTagAssignmentList, TagCategory, TagCategoryList, TagValue, \
    TagValueFilter, TagValueFilters, TagValueList
from tenable_io.api.tags import AssetAssignmentsRequest, TagCategoryRequest, TagValueRequest
        assert isinstance(category, TagCategory), u'The list should be of type `TagCategory`.'


@pytest.mark.vcr()
def test_tags_category(client, new_tag_category):
    category = new_tag_category
    fetched_category = client.tags_api.category(category.uuid)
    assert fetched_category.uuid == category.uuid, \
        u'The `category` method returns tag category with different uuid.'
    assert isinstance(fetched_category, TagCategory), u'The `category` method did not return type `TagCategory`.'


@pytest.mark.vcr()
def test_tags_category_delete(client, new_tag_category):
    assert client.tags_api.delete_category(new_tag_category.uuid), u'The tag category was not deleted.'


@pytest.mark.vcr()
def test_tags_category_edit(client, new_tag_category):
    edit_name = 'test_category_edit'
    edit_category = client.tags_api.edit_category(new_tag_category.uuid, TagCategoryRequest(
        name=edit_name
    ))
    assert isinstance(edit_category, TagCategory), u'The `edit_category` method did not return type `TagCategory`.'
    assert edit_category.name == edit_name, u'The tag category edit was not successful.'


@pytest.mark.vcr()
def test_tags_category_values(client, new_tag_category):
    category = new_tag_category
    value = create_value(client, category.name)
    category_values = client.tags_api.category_values(category.uuid)
    assert isinstance(category_values, TagValueList), \
        u'The `category_values` method did not return type `TagValueList`.'
    assert len(category_values.values), u'Expected a single value for the category.'
    assert category_values.values[0].value == value.value, u'Expected created value to be returned.'


@pytest.mark.vcr()
def test_tags_category_value(client, new_tag_category):
    category = new_tag_category
    value = create_value(client, category.name)
    category_value = client.tags_api.category_value(category.uuid, value.uuid)
    assert isinstance(category_value, TagValue), \
        u'The `category_value` method did not return type `TagValue`.'
    assert category_value.value == value.value, u'Expected created value to be returned.'


@pytest.mark.vcr()
def test_tags_category_counts(client, new_tag_category):
    counts = client.tags_api.category_counts(new_tag_category.uuid)
    assert counts is None, u'Expected no assignments for the new category.'


@pytest.mark.vcr()
def test_tags_value_create(client, new_tag_category):
    value = create_value(client, new_tag_category.uuid)
    assert isinstance(value, TagValue), u'The `create_value` method did not return type `TagValue`.'


@pytest.mark.vcr()
def test_tags_values(client):
    value_list = client.tags_api.values()
    assert isinstance(value_list, TagValueList), u'The `values` method did not return type `TagValueList`.'

    for value in value_list.values:
        assert isinstance(value, TagValue), u'The list should be of type `TagValue`.'


@pytest.mark.vcr()
def test_tags_value_delete(client, new_tag_category):
    value = create_value(client, new_tag_category.uuid)
    assert client.tags_api.delete_value(value.uuid), u'The tag value was not deleted.'


@pytest.mark.vcr()
def test_tags_value_edit(client, new_tag_category):
    value = create_value(client, new_tag_category.uuid)
    edit_name = 'test_value_edit'
    edit_value = client.tags_api.edit_value(value.uuid, TagValueRequest(
        value=edit_name
    ))
    assert isinstance(edit_value, TagValue), u'The `edit_value` method did not return type `TagValue`.'
    assert edit_value.value == edit_name, u'The tag value edit was not successful.'


@pytest.mark.vcr()
def test_tags_value_counts(client, new_tag_category):
    value = create_value(client, new_tag_category.uuid)
    counts = client.tags_api.value_counts(value.uuid)
    assert counts is None, u'Expected no assignments for the new value.'


@pytest.mark.vcr()
def test_tags_value_bulk_delete(client, new_tag_category):
    category = new_tag_category
    value1 = create_value(client, category.uuid)
    value2 = create_value(client, category.uuid)
    assert client.tags_api.value_delete_requests([value1.uuid, value2.uuid]), u'The tag values were not deleted.'


@pytest.mark.vcr()
def test_tags_asset_tag_assignments(client, new_tag_category):
    value = create_value(client, new_tag_category.uuid)
    asset = client.assets_api.list().assets[0]
    # check assignments
    asset_assignments = client.tags_api.asset_tag_assignments(asset.id)
    assert isinstance(asset_assignments, AssetTagAssignmentList), \
        u'The `asset_tag_assignments` method did not return type `AssetTagAssignmentList`.'
    assert len(asset_assignments.tags) == 0, u'Expected 0 assignments for asset.'
    # assign value to asset
    assignment_request = AssetAssignmentsRequest(
        action='add',
        assets=[asset.id],
        tags=[value.uuid]
    )
    assert client.tags_api.edit_asset_tag_assignments(assignment_request), \
        u'The tag assignment request was not successful.'
    # check assignments again
    asset_assignments = client.tags_api.asset_tag_assignments(asset.id)

    assert isinstance(asset_assignments, AssetTagAssignmentList), \